I've defined the infinite list of infinite lists pathCounts
and the infinite list of finite lists pathCounts'
:
import Data.Function (fix)
nextRow xs = fix $ \ys -> zipWith (+) xs (0:ys)
pathCounts = repeat 1 : map nextRow pathCounts
pathCounts' = map (take 100) pathCounts
Dropping into ghci, if I haven't evaluated either at all, I can use :p
on either successfully:
ghci> :p pathCounts
pathCounts = (_t1::[[Integer]])
ghci> :p pathCounts'
pathCounts' = (_t2::[[Integer]])
But if I evaluate pathCounts'
partially, then :p
freezes on pathCounts
while still succeeding on pathCounts'
:
ghci> head . head $ pathCounts'
1
ghci> :p pathCounts'
pathCounts' = (1 : (_t4::[Integer])) : (_t5::[[Integer]])
ghci> :p pathCounts
^CInterrupted.
I'd expect :p pathCounts
to print the same as :p pathCounts'
, as I've only partially evaluated it. Why isn't it working?
Ah, but that's the interesting bit! You have, in fact, fully evaluated the (infinitely long) head of
pathCounts
. Let's take a slightly smaller example to simplify the discussion:I claim that after fully evaluating
head v
, in factv
is also fully evaluated. This is because, in memory,v
is a cyclic singly-linked list. So if you've evaluated enough to know the first element, there is nothing left to evaluate!The result is that when you ask to
:print
it, GHC duly attempts to construct a string representing all of the evaluated portion of the structure -- and obviously can't, since it will never stop traversing. (:p
simply doesn't have a way to indicate sharing in a structure.)Compare:
Although you have only requested evaluation of the first part of
v
, in fact all ofv
has been evaluated here, so:p
prints it all -- and doesn't indicate the sharing that exists between the first and second parts.Now, how come
pathCounts'
doesn't have the same problem? Well, the thing there is that, althoughmap f (repeat x) = repeat (f x)
is a denotationally correct equation, in the GHC implementation of Haskell, that equation isn't operationally sound -- and:p
is all about the operational semantics and thumbs its nose at the denotational semantics. In particular,map
doesn't (can't) observe the sharing present inrepeat x
; hence it produces a non-cyclic infinite list. Sincemap f (repeat x)
has less sharing, forcing part ofmap f (repeat x)
doesn't result in an in-memory representation that is as fully evaluated.