GHC não memoriza funções.
No entanto, ele calcula qualquer expressão dada no código no máximo uma vez por vez que a expressão lambda circundante é inserida, ou no máximo uma vez se estiver no nível superior. Determinar onde estão as expressões lambda pode ser um pouco complicado quando você usa açúcar sintático como em seu exemplo, então vamos convertê-los em sintaxe desugared equivalente:
m1' = (!!) (filter odd [1..]) -- NB: See below!
m2' = \n -> (!!) (filter odd [1..]) n
(Nota: O relatório Haskell 98 realmente descreve uma seção esquerda do operador como (a %)
como equivalente a \b -> (%) a b
, mas o GHC a desugou (%) a
. Eles são tecnicamente diferentes porque podem ser diferenciados por seq
. Acho que devo ter enviado um tíquete do GHC Trac sobre isso.)
Dado isso, você pode ver que em m1'
, a expressão filter odd [1..]
não está contida em nenhuma expressão lambda, então ela só será calculada uma vez por execução de seu programa, enquanto emm2'
, filter odd [1..]
será computado cada vez que a expressão lambda for inserida, ou seja, em cada chamada de m2'
. Isso explica a diferença de tempo que você está vendo.
Na verdade, algumas versões do GHC, com certas opções de otimização, irão compartilhar mais valores do que a descrição acima indica. Isso pode ser problemático em algumas situações. Por exemplo, considere a função
f = \x -> let y = [1..30000000] in foldl' (+) 0 (y ++ [x])
GHC pode perceber que y
não depende dex
e reescrever a função para
f = let y = [1..30000000] in \x -> foldl' (+) 0 (y ++ [x])
Neste caso, a nova versão é muito menos eficiente porque terá que ler cerca de 1 GB da memória onde y
está armazenada, enquanto a versão original rodaria em espaço constante e caberia no cache do processador. Na verdade, no GHC 6.12.1, a função f
é quase duas vezes mais rápida quando compilada sem otimizações do que com a qual é compilada -O2
.
seq
m1 10000000). No entanto, há uma diferença quando nenhum sinalizador de otimização é especificado. E ambas as variantes do seu "f" têm residência máxima de 5356 bytes, independentemente da otimização, a propósito (com menos alocação total quando -O2 é usado).