Note [exprIsHNF for function applications]

GHC/Core/Utils.hs:2301 compiler

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

Referenced by 2