Я пытался понять, как работает совместное вычисление в Haskell. Насколько я понимаю, общие вычисления без баллов должны оцениваться только один раз (любезно предоставлено CSE).
(A) Например, рассмотрим следующий код и его вывод:
*Main> let foo = trace "eval foo" 5 in foo + foo
eval foo
10
*Main> let foo' = \x -> trace "eval foo'" x in (foo' 5) + (foo' 5)
eval foo'
eval foo'
10
Как и ожидалось, foo
оценивается только один раз (CSE, вероятно, начинает действовать), тогда как foo'
оценивается лениво дважды. Это хорошо. Я попробовал вышеупомянутое использование GHCi, версия 7.6.3. Затем я попробовал тот же код в GHCi версии 8.6.5, но вместо этого получил следующий результат:
*Main> let foo = trace "eval foo" 5 in foo + foo
eval foo
eval foo
10
Обратите внимание, что foo
оценивается дважды.
(B) Аналогично, с GHCi, версия 7.6.3:
*Main> let goo = const (trace "eval goo" 5) in goo () + goo ()
eval goo
10
но GHCi, версия 8.6.5, дважды оценивает goo
:
*Main> let goo = const (trace "eval goo" 5) in goo () + goo ()
eval goo
eval goo
10
(C) Наконец, обе версии дают одинаковый результат для приведенного ниже:
*Main> let foo_wrapper x = let foo = trace "eval foo" x in foo + foo
*Main> foo_wrapper 5
eval foo
10
Интересно, были ли некоторые оптимизации по умолчанию отключены в GHCi-8 или побочные эффекты от trace
заставляют foo
быть оценены как-то дважды? Или была проблема в GHCi-7? Как GHCi должен вести себя с такими выражениями, как (A) и (B)?
(Обновление 1)
Для сценария (C) рассмотрите следующие прогоны в GHCi-8 (с основным отличием во втором аргументе trace
):
*Main> let foo_wrapper x = let foo = trace "eval foo" x in foo + foo
*Main> foo_wrapper 5
eval foo
10
*Main> :t (foo_wrapper 5)
(foo_wrapper 5) :: Num a => a
*Main> let foo_wrapper' x = let foo = trace "eval foo" 5 in foo + foo
*Main> foo_wrapper' ()
eval foo
eval foo
10
*Main> :t (foo_wrapper' ())
(foo_wrapper' ()) :: Num a => a