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