Note [exprIsHNF for function applications]
Consider an application with an Id head where the argument is a redex:
f <redex>
Is this expression a value?
The answer depends on the type of `f`, its arity and whether or not it is a
strict data constructor. The decision diagram is as follows:
* If <redex> is unlifted, it is *not* a value (regardless of arity!)
* Otherwise, <redex> is lifted.
Does its `idArity` (a lower bound on the actual arity)
exceed the number of actual arguments (= 1)?
* If so, it is a PAP and thus a value
* If not, it is a saturated call.
Is it a lazy data constructor? Then it is a value.
Is it a strict data constructor? Then it is *not* a value. (See also Note [Strict fields in Core].)
Otherwise, it is a regular, possibly saturated function call, and hence *not* a value.
The code in exprIsHNF is tweaked for efficiency, hence it delays the
unliftedness check after the arity check.
Here are a few examples (enshrined in testcase AppIsHNF) to bring home this
point. Let us say that
f :: Int# -> Int -> Int -> Int, with idArity 3
expensive# :: Int -> Int# -- unlifted result
expensive :: Int -> Int -- lifted result
data T where
K1 :: !Int -> Int -> T -- strict field
K2 :: Int# -> Int -> T -- unlifted field
Now consider
f (expensive# 1) 2 -- Not HNF
f 1# (expensive 2) -- HNF
K1 1 (expensive 2) -- HNF
K1 (expensive 1) 2 -- Not HNF
K1 (expensive 1) -- HNF (!)
K2 1# (expensive 1) -- HNF
K2 (expensive# 1) 2 -- Not HNF
K2 (expensive# 1) -- Not HNF (!)
Note that the cases marked (!) exemplify that strict fields are different to
unlifted fields when considering partial applications: Unlifted fields are
evaluated eagerly whereas evaluation of strict fields is delayed until the call
is saturated. References 1
- Strict fields in Core GHC.Core
Referenced by 2
- Strict fields in Core GHC.Core
- GHC.Core.Utils call site