You’ve been bitten by the fact that, since GHC 7.8.1, the monomorphism restriction is disabled in GHCi by default. This means that the x
binding is generalized to a polymorphic, typeclass-constrained binding with the type
x :: Num a => a
since 1
is a polymorphic number literal. Even though this type does not include any function arrows (->
), at runtime, it behaves more like a function than as a value, since it is really a function that accepts a Num
typeclass dictionary and uses it to construct its result.
You can avoid this by explicitly annotating the literal to avoid the polymorphism:
ghci> let x = trace "one" (1 :: Integer) in (x, x)
(one
1,1)
ghci> let !x = trace "one" (1 :: Integer) in 1
one
1
Normally, the aforementioned monomorphism restriction is in place, precisely to prevent this kind of confusion where a binding that is syntactically a value definition can have its RHS evaluated multiple times. The linked answer describes some of the tradeoffs of the restriction, but if you want, you can switch it back on, which will make your original examples do what you expect:
ghci> :set -XMonomorphismRestriction
ghci> let x = trace "one" 1 in (x, x)
(one
1,1)
ghci> let !x = trace "one" 1 in 1
one
1