Using DeepSeq for exception ordering

Edward Z. Yang ezyang at MIT.EDU
Thu Nov 8 02:45:56 CET 2012

Hello Simon,

I think the confusion here is focused on what exactly it is that
the NFData class offers:

class NFData a where
    rnf :: a -> ()

rnf can be thought of a function which produces a thunk (for unit)
which, when forced, fully evaluates the function.  With this in hand,
it's pretty clear how to use evaluate to enforce ordering:

    evaluate (rnf ('a': throw exceptionA))

One could imagine defining:

    deepSeqEvaluate :: NFData a => a -> IO ()
    deepSeqEvaluate = evaluate . rnf

In general, the right way to think about the semantics here is to
distinguish between evaluation as an explicit effect (evaluate) and
evaluation as a side effect of running IO (when you x `seq` return ()).
They're distinct, and the latter doesn't give you ordering guarantees.
This applies even when DeepSeq is involved.


Excerpts from Simon Hengel's message of Wed Nov 07 05:49:21 -0800 2012:
> Hi,
> I'm puzzled whether it is feasible to use existing NFData instances for
> exception ordering.
> Here is some code that won't work:
>     return $!! 'a' : throw exceptionA
>     throwIO exceptionB
> Here GHC makes a non-deterministic choice between exceptionA and
> exceptionB.  The reason is that the standard DeepSeq instances use
> `seq`, and `seq` does not help with exception ordering**.
> I tried several things (ghc-7.4.2 with -O2), and the following seems to
> order the exceptions for this particular case:
>     (evaluate . force) ('a' : throw exceptionA)
>     throwIO exceptionB
> But I'm a little bit worried that this may not hold in general, e.g.
>     (return $!! 'a' : throw exceptionA) >>= evaluate
>     throwIO exceptionB
> results in exceptionB.  I think my main issue here is that I do not
> properly understand how seq and seq# (which is used by evaluate) do
> interact with each other.  And how I can reason about code that uses
> both.
> The question is really whether it is "somehow" feasible to use existing
> NFData instances to order exceptions.  Or would we need to define a
> separate type class + instances for that, e.g.:
>     class DeepEvaluate a where
>       deepEvaluate :: a -> IO a
>       deepEvaluate = evaluate
>     instance DeepEvaluate Char where
>     instance DeepEvaluate a => DeepEvaluate [a] where
>       deepEvaluate = mapM deepEvaluate
> If you have any related ideas or thoughts, I'd love to hear about them.
> Cheers,
> Simon
> ** This is desired behavior, see the discussion at

More information about the Glasgow-haskell-users mailing list