This question arises from a challenge Brent Yorgey posed at OPLSS: write a function f :: (Int -> Int) -> Bool
that distinguishes f undefined
from f (\x -> undefined)
. All of our answers either used seq
or something like bang patterns that desugar into seq
. For example:
f :: (Int -> Int) -> Bool f g = g `seq` True *Main> f undefined *** Exception: Prelude.undefined *Main> f (\x -> undefined) True
The GHC commentary on seq
says that
e1 `seq` e2
used to desugar into
case e1 of { _ -> e2 }
so I tried desugaring manually. It didn't work:
f' g = case g of { _ -> True } *Main> f' undefined True *Main> f' (\x -> undefined) True
Does this behavior depend on the more complex seq
described at the end of the commentary, and if so, how does it work? Could such an f
be written without these primitives?
x `seq` e2 ==> case seq# x RW of (# x, _ #) -> e2 -- Note shadowing! e1 `seq` e2 ==> case seq# x RW of (# _, _ #) -> e2
Definition: The seq R function generates a sequence of numeric values.
seq
cannot be implemented in Haskell. Instead, it is a primitive "hook" into evaluation to weak-head normal form in whatever runtime your Haskell is running on. E.g. on GHC it is compiled to a case
in GHC Core, which triggers evaluation to the outermost constructor.
Since it can't be implemented in pure Haskell, it is defined (in GHC) as a primop:
pseudoop "seq" a -> b -> b { Evaluates its first argument to head normal form, and then returns its second argument as the result. }
Since functions don't have a normal form, seq
halts evaluation once it reaches one.
Magically available to the compiler. The same goes for other primitives like par
or unsafeCoerce
, the RealWorld
token, forkOn
and so on. All the useful stuff.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With