Suppose I write in GHCi:
GHCi> let x = 1 + 2 :: Integer
GHCi> seq x ()
GHCi> :sprint x
GHCi prints x = 3 as naturally expected.
However,
GHCi> let x = 1 + 2
GHCi> seq x ()
GHCi> :sprint x
yields x = _
The sole difference between the two expressions are their types (Integer vs Num a => a). My question is what exactly happens, and why is seemingly x not evaluated in the latter example.
The main issue is that
defines a polymorphic value of type
forall a. Num a => a, and that is something which evaluates similarly to a function.Each use of
xcan be made at a different type, e.g.x :: Int,x :: Integer,x :: Doubleand so on. These results are not "cached" in any way, but recomputed every time, as ifxwere a function which is called multiple times, so to speak.Indeed, a common implementation of type classes implements such a polymorphic
xas a functionwhere the
NumDict aargument above is added by the compiler automatically, and carries information aboutabeing aNumtype, including how to perform addition, how to interpret integer literals insidea, and so on. This is called the "dictionary-passing" implementation.So, using a polymorphic
xmultiple times indeed corresponds to invoking a function multiple times, causing recomputation. To avoid this, the (dreaded) Monomorphism Restriction was introduced in Haskell, forcingxto be monomorphic instead. The MR is not a perfect solution, and can create some surprising type errors in certain cases.To alleviate this issue, the MR is disabled by default in GHCi, since in GHCi we don't care that much about performance -- usability is more important there. This however causes the recomputation to reappear, as you discovered.