Why doesn't GHC provide (isWHNF :: a -> IO Bool)?

I’m quite comfortable with laziness as is in the language, there’s just one thing I find missing: an ability to guarantee that any given value is correctly evaluated.

A good example of a bad way to do this is deepseq: rnf always traverses the entire data structure and gives no feedback on whether it evaluated anything. In an application that correctly evaluates data this function is a complete waste of time.

Moving this functionality to a debug build can be done with an assert, so that’s easy.

The question that remains then is how to check whether a data structure is evaluated. Per the nothunks library, the answer (here) seems to be using GHC.Exts.Heap.getBoxedClosureData to get a copy of an internal representation and analyzing that. This looks ad-hoc and I imagine is also relatively slow with all the repackaging.

So why isn’t there isWHNF :: a -> IO Bool function in GHC internals that copies what seq does without forcing the thunk (similar relationship to one between tryReadMVar and readMVar)?

You can kind of define it yourself:

{- cabal:
build-depends: base, ghc-heap
-}
import GHC.Exts.Heap

isWHNF :: a -> IO Bool
isWHNF x = do
  closure <- getBoxedClosureData $ asBox x
  pure $! case closure of
    ThunkClosure{} -> False
    SelectorClosure{} -> False
    APClosure{} -> False
    BlackholeClosure{} -> False
    _ -> True

Edit: Ah that’s what nothunks does and you already mentioned it.

I don’t think there’s a reason why it was not included, except for “nobody did it yet”. That’s certainly a good addition to ghc-internal. :slight_smile: