GHC no memoriza funciones.
Sin embargo, calcula cualquier expresión dada en el código como máximo una vez cada vez que se ingresa su expresión lambda circundante, o como máximo una vez si está en el nivel superior. Determinar dónde están las expresiones lambda puede ser un poco complicado cuando usa azúcar sintáctico como en su ejemplo, así que conviértalo a una sintaxis desazucarada equivalente:
m1' = (!!) (filter odd [1..]) -- NB: See below!
m2' = \n -> (!!) (filter odd [1..]) n
(Nota: el informe Haskell 98 en realidad describe una sección de operador izquierda (a %)
como equivalente a \b -> (%) a b
, pero GHC lo desugar (%) a
. Estos son técnicamente diferentes porque se pueden distinguir por seq
. Creo que podría haber enviado un ticket de GHC Trac sobre esto).
Dado esto, puede ver que en m1'
, la expresión filter odd [1..]
no está contenida en ninguna expresión lambda, por lo que solo se calculará una vez por ejecución de su programa, mientras que en m2'
, filter odd [1..]
se calculará cada vez que se ingrese la expresión lambda, es decir, en cada llamada de m2'
. Eso explica la diferencia en el tiempo que está viendo.
De hecho, algunas versiones de GHC, con ciertas opciones de optimización, compartirán más valores de los que indica la descripción anterior. Esto puede resultar problemático en algunas situaciones. Por ejemplo, considere la función
f = \x -> let y = [1..30000000] in foldl' (+) 0 (y ++ [x])
GHC puede notar que y
no depende x
y reescribe la función para
f = let y = [1..30000000] in \x -> foldl' (+) 0 (y ++ [x])
En este caso, la nueva versión es mucho menos eficiente porque tendrá que leer alrededor de 1 GB de la memoria donde y
se almacena, mientras que la versión original se ejecutaría en un espacio constante y cabría en la caché del procesador. De hecho, en GHC 6.12.1, la función f
es casi dos veces más rápida cuando se compila sin optimizaciones de lo que se compila -O2
.
seq
m1 10000000). Sin embargo, hay una diferencia cuando no se especifica ningún indicador de optimización. Y ambas variantes de su "f" tienen una residencia máxima de 5356 bytes independientemente de la optimización, por cierto (con menos asignación total cuando se usa -O2).