GHC не запоминает функции.
Однако он вычисляет любое заданное выражение в коде не чаще одного раза за раз, когда вводится окружающее его лямбда-выражение, или не более одного раза, если оно находится на верхнем уровне. Определение того, где находятся лямбда-выражения, может быть немного сложным, если вы используете синтаксический сахар, как в вашем примере, поэтому давайте преобразуем их в эквивалентный синтаксис без сахара:
m1' = (!!) (filter odd [1..]) -- NB: See below!
m2' = \n -> (!!) (filter odd [1..]) n
(Примечание: отчет Haskell 98 на самом деле описывает левую секцию оператора (a %)
как эквивалент \b -> (%) a b
, но GHC обессахаривает ее (%) a
. Технически они отличаются, потому что их можно различить по seq
. Думаю, я мог бы подать заявку на GHC Trac по этому поводу.)
Учитывая это, вы можете видеть, что in m1'
, выражение filter odd [1..]
не содержится ни в одном лямбда-выражении, поэтому оно будет вычисляться только один раз за запуск вашей программы, в то время как in m2'
, filter odd [1..]
будет вычисляться каждый раз при вводе лямбда-выражения, т. Е. на каждый вызов m2'
. Это объясняет разницу во времени, которую вы видите.
Фактически, некоторые версии GHC с определенными параметрами оптимизации будут иметь больше значений, чем указано в приведенном выше описании. В некоторых ситуациях это может быть проблематично. Например, рассмотрим функцию
f = \x -> let y = [1..30000000] in foldl' (+) 0 (y ++ [x])
GHC может заметить, что y
это не зависит от x
и переписать функцию на
f = let y = [1..30000000] in \x -> foldl' (+) 0 (y ++ [x])
В этом случае новая версия намного менее эффективна, потому что ей придется считывать около 1 ГБ из памяти, в которой y
она хранится, в то время как исходная версия будет работать в постоянном пространстве и поместиться в кеш-память процессора. Фактически, в GHC 6.12.1 функция f
почти в два раза быстрее при компиляции без оптимизации, чем при компиляции -O2
.
seq
m1 10000000). Однако есть разница, когда не указан флаг оптимизации. И оба варианта вашего "f" имеют максимальное размещение 5356 байт независимо от оптимизации, кстати (с меньшим общим распределением при использовании -O2).