为什么:当我给它这个核心值时,p冻结在GHCi中?

ram*_*ion 12 haskell ghci corecursion

我已经定义了无限列表的无限列表pathCounts和有限列表的无限列表pathCounts':

import Data.Function (fix)

nextRow xs = fix $ \ys -> zipWith (+) xs (0:ys)

pathCounts = repeat 1 : map nextRow pathCounts
pathCounts' = map (take 100) pathCounts
Run Code Online (Sandbox Code Playgroud)

放入ghci,如果我根本没有评估,我可以:p成功使用:

ghci> :p pathCounts
pathCounts = (_t1::[[Integer]])
ghci> :p pathCounts'
pathCounts' = (_t2::[[Integer]])
Run Code Online (Sandbox Code Playgroud)

但如果我pathCounts'部分评估,那么:ppathCounts继续成功的同时冻结pathCounts':

ghci> head . head $ pathCounts'
1
ghci> :p pathCounts'
pathCounts' = (1 : (_t4::[Integer])) : (_t5::[[Integer]])
ghci> :p pathCounts
^CInterrupted.
Run Code Online (Sandbox Code Playgroud)

我希望:p pathCounts打印相同:p pathCounts',因为我只是部分评估它.为什么不工作?

Dan*_*ner 8

我希望:p pathCounts打印相同:p pathCounts',因为我只是部分评估它.

啊,但那是有趣的一点!事实上,你已经完全评估了(无限长)的头部pathCounts.让我们用一个稍小的例子来简化讨论:

> let v = repeat 1 :: [Int]
> head v
1
> :p v
^C
Run Code Online (Sandbox Code Playgroud)

我声称在完全评估之后head v,事实上v也是完全评估的.这是因为,在存储器中,v是循环单链表.因此,如果您已经足够了解第一个元素,那么就没有什么可以评估了!

结果是,当你提出要求:print时,GHC正式尝试构造一个表示结构的所有评估部分的字符串 - 显然不能,因为它永远不会停止遍历.(:p根本没有办法表明在结构中共享.)

相比:

> let x = 1 :: Int; v = (x, x)
> fst v
1
> :p v
(1,1)
Run Code Online (Sandbox Code Playgroud)

虽然您只要求对第一部分进行评估,但v事实上所有部分v都已在此处进行了评估,因此将:p其全部打印出来 - 并不表示第一部分和第二部分之间存在共享.

现在,怎么pathCounts'没有同样的问题?好吧,有一点是,尽管map f (repeat x) = repeat (f x)是一个指称正确的方程式,但在Haskell的GHC实现中,该方程:p在操作上并不合理- 而且完全是关于操作语义和拇指在指称语义上.特别是,map不能(不能)观察到的共享repeat x; 因此它产生一个非循环的无限列表.由于map f (repeat x)共享较少,强制部分map f (repeat x)不会导致内存表示完全评估.