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 про це.)
Враховуючи це, ви можете бачити це в m1'
виразіfilter odd [1..]
не міститься в жодному лямбда-виразі, тому він буде обчислюватися лише один раз на запуск вашої програми, в той час як в 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).