Background
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
Question
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
There is a more higher-level description of STG-machine in How to make a fast curry: push/enter vs eval/apply
Figure 2 contains rule CASEANY that works for functions. In this paper “is a value” proposition means either:
Unboxed values, including literals are treated specially, more information can be found in Unboxed values as first class citizens
All these are implementation details and are hidden inside compiler (GHC). Haskell’s case expression doesn’t force it’s scrutineer, only pattern-matching and seq do.