GHC non memorizza le funzioni.
Tuttavia, calcola qualsiasi espressione data nel codice al massimo una volta ogni volta che viene inserita l'espressione lambda circostante, o al massimo una volta se si trova al livello superiore. Determinare dove si trovano le espressioni lambda può essere un po 'complicato quando usi lo zucchero sintattico come nel tuo esempio, quindi convertiamole nella sintassi desugared equivalente:
m1' = (!!) (filter odd [1..]) -- NB: See below!
m2' = \n -> (!!) (filter odd [1..]) n
(Nota: il rapporto Haskell 98 descrive in realtà una sezione operatore di sinistra (a %)
come equivalente a \b -> (%) a b
, ma GHC la designa (%) a
. Questi sono tecnicamente diversi perché possono essere distinti seq
. Penso che potrei aver inviato un ticket GHC Trac su questo.)
Detto questo, puoi vedere che in m1'
, l'espressione filter odd [1..]
non è contenuta in nessuna espressione lambda, quindi verrà calcolata solo una volta per esecuzione del tuo programma, mentre in m2'
, filter odd [1..]
verrà calcolata ogni volta che viene inserita l'espressione lambda, ovvero su ogni chiamata di m2'
. Questo spiega la differenza di tempistica che stai vedendo.
In realtà, alcune versioni di GHC, con alcune opzioni di ottimizzazione, condivideranno più valori di quelli indicati nella descrizione sopra. Questo può essere problematico in alcune situazioni. Ad esempio, considera la funzione
f = \x -> let y = [1..30000000] in foldl' (+) 0 (y ++ [x])
GHC potrebbe notare che y
non dipende da x
e riscrive la funzione
f = let y = [1..30000000] in \x -> foldl' (+) 0 (y ++ [x])
In questo caso, la nuova versione è molto meno efficiente perché dovrà leggere circa 1 GB dalla memoria in cui y
è archiviato, mentre la versione originale girerebbe in uno spazio costante e starebbe nella cache del processore. In effetti, con GHC 6.12.1, la funzione f
è quasi il doppio più veloce quando viene compilata senza ottimizzazioni rispetto a quella con cui viene compilata -O2
.
seq
m1 10000000). Tuttavia, c'è una differenza quando non viene specificato alcun flag di ottimizzazione. Ed entrambe le varianti della tua "f" hanno una residenza massima di 5356 byte indipendentemente dall'ottimizzazione, tra l'altro (con meno allocazione totale quando si usa -O2).