Понимание различного поведения thunks, когда GHCi позволяет связывать

Я играл с некоторыми примерами из книги Саймона Марлоу о параллельном и параллельном программировании в Haskell и наткнулся на интересное поведение, которое я не совсем понимаю. Это действительно о том, что я пытаюсь понять некоторые внутренние принципы GHC.

Допустим, я делаю следующее в REPL:

λ» let x = 1 + 2 :: Int
λ» let z = (x,x)
λ» :sprint x
x = _
λ» :sprint z
z = (_,_)
λ» seq x ()
()
λ» :sprint z
z = (3,3)

Хорошо, это почти то, что я ожидал, за исключением того, что z уже оценен в WHNF. Давайте напишем похожую программу и поместим ее в файл:

module Thunk where

import Debug.Trace

x :: Int
x = trace "add" $ 1 + 2

z :: (Int,Int)
z = (x,x)

И возиться с этим в GHCi:

λ» :sprint x
x = _
λ» :sprint z
z = _
λ» seq x ()
add
()
λ» :sprint z
z = _
λ» seq z ()
()
λ» z
(3,3)

Так что это ведет себя немного по-другому: z не оценивается WHNF заранее. Мой вопрос:

Почему z оценивается в WHNF в REPL при выполнении let z = (x,x) но не при загрузке определения из файла. Я подозреваю, что это как-то связано с привязкой к шаблону, но я не знаю, где искать это для пояснения (возможно, я просто совершенно не прав). Я ожидал бы, что это так или иначе будет вести себя как пример в файле.

Любые указатели или краткое объяснение, почему это происходит?

1 ответ

Так как (,) является конструктором, разница не имеет значения для семантики Haskell (:sprint дает доступ к деталям внутренней реализации Thunk, поэтому не учитывается.) Так что это вопрос о том, какие оптимизации и компромиссы делает GHC при компиляции (x,x) в разных позициях. Кто-то еще может знать точную причину в этих случаях.

Другие вопросы по тегам