haskelllazy-evaluationweak-head-normal-form

Expression Evaluation In Haskell: Fixing the type of a sub-expression causes parent expression to be evaluated to different degrees


I am not able to explain the following behavior:

Prelude> let x = 1 + 2
Prelude> let y = (x,x)
Prelude> :sprint y
Prelude> y = _

Now when I specify a type for x:

Prelude> let x = 1 + 2 ::Int
Prelude> let y = (x,x)
Prelude> :sprint y
Prelude> y = (_,_)

Why does the specification of x's type force y to its weak head normal form (WHNF)?

I accidentally discovered this behavior while reading Simon Marlow's Parallel and Concurrent Programming In Haskell.


Solution

  • Here's an informed guess. In your first example,

    x :: Num a => a
    

    So

    y :: Num a => (a, a)
    

    In GHC core, this y is a function that takes a Num dictionary and gives a pair. If you were to evaluate y, then GHCi would default it for you and apply the Integer dictionary. But from what you've shown, it seems likely that doesn't happen with sprint. Thus you don't yet have a pair; you have a function that produces one.

    When you specialize to Int, the dictionary is applied to x, so you get

    x :: Int
    y :: (Int, Int)
    

    Instead of a function from a dictionary, x is now a thunk. Now no dictionary needs to be applied to evaluate y! y is just the application of the pair constructor to two pointers to the x thunk. Applying a constructor doesn't count as computation, so it's never delayed lazily.