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.
Cheers,
Edward
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
> http://hackage.haskell.org/trac/ghc/ticket/5129
>
More information about the Glasgow-haskell-users
mailing list