Понимание различного поведения 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)
в разных позициях. Кто-то еще может знать точную причину в этих случаях.