I'd expect
:p pathCounts
to print the same as:p pathCounts'
, as I've only partially evaluated it.
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:
> let v = repeat 1 :: [Int]
> head v
1
> :p v
^C
I claim that after fully evaluating head v
, in fact v
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:
> let x = 1 :: Int; v = (x, x)
> fst v
1
> :p v
(1,1)
Although you have only requested evaluation of the first part of v
, in fact all of v
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, although map 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 in repeat x
; hence it produces a non-cyclic infinite list. Since map f (repeat x)
has less sharing, forcing part of map f (repeat x)
doesn't result in an in-memory representation that is as fully evaluated.