Simon Hengel | 7 Nov 14:49 2012
Picon

Using DeepSeq for exception ordering

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
(Continue reading)

Edward Z. Yang | 8 Nov 02:45 2012
Picon

Re: Using DeepSeq for exception ordering

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,
(Continue reading)

Simon Hengel | 8 Nov 13:12 2012
Picon

Re: Using DeepSeq for exception ordering

Hi Edward,
thanks a lot for your reply.

> 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))

So if I understand correctly, then if I have

  evaluate (x_1 `seq` x_2 `seq` x_3 `seq` ... `seq` x_n)
  throwIO exceptionB

it is guaranteed that exceptionB can only happens if none of the xs are
"exceptional".

I was just going to say that I can give at least one counterexample
where this does not hold:

  evaluate (('a' : undefined) `deepseq` return () :: IO ())
  throwIO exceptionB

But then I realized that here exceptionA is optimized away altogether.
For me this smells like a bug.  Is this related to [1]?

Cheers,
Simon

[1] http://hackage.haskell.org/trac/ghc/ticket/2273
(Continue reading)

Albert Y. C. Lai | 8 Nov 18:24 2012
Picon

Re: Using DeepSeq for exception ordering

On 12-11-08 07:12 AM, Simon Hengel wrote:
> I was just going to say that I can give at least one counterexample
> where this does not hold:
>
>    evaluate (('a' : undefined) `deepseq` return () :: IO ())
>    throwIO exceptionB
>
> But then I realized that here exceptionA is optimized away altogether.
> For me this smells like a bug.  Is this related to [1]?
>
> [1] http://hackage.haskell.org/trac/ghc/ticket/2273

Interesting. A few more tests (all GHC 7.4.2, linux, x86 32-bit, use 
"ghc -O" to compile):

The following cases throw A:

import Control.DeepSeq
import Control.Exception

main = do
   evaluate (('a' : error "A") `deepseq` return () :: Maybe ())
   throwIO (userError "B")

main = do
   evaluate (('a' : error "A") `deepseq` ())
   throwIO (userError "B")

main = do
   evaluate (('a' : error "A") `deepseq` True)
(Continue reading)

Nicolas Frisby | 8 Nov 19:01 2012
Picon

Re: Using DeepSeq for exception ordering

And the important observation is: all of them throw A if interpreted in ghci or compiled without -O, right?


On Thu, Nov 8, 2012 at 11:24 AM, Albert Y. C. Lai <trebla <at> vex.net> wrote:
On 12-11-08 07:12 AM, Simon Hengel wrote:
I was just going to say that I can give at least one counterexample
where this does not hold:

   evaluate (('a' : undefined) `deepseq` return () :: IO ())
   throwIO exceptionB

But then I realized that here exceptionA is optimized away altogether.
For me this smells like a bug.  Is this related to [1]?

[1] http://hackage.haskell.org/trac/ghc/ticket/2273

Interesting. A few more tests (all GHC 7.4.2, linux, x86 32-bit, use "ghc -O" to compile):

The following cases throw A:

import Control.DeepSeq
import Control.Exception

main = do
  evaluate (('a' : error "A") `deepseq` return () :: Maybe ())
  throwIO (userError "B")

main = do
  evaluate (('a' : error "A") `deepseq` ())
  throwIO (userError "B")

main = do
  evaluate (('a' : error "A") `deepseq` True)
  throwIO (userError "B")

main = do
  x <- evaluate (('a' : error "A") `deepseq` putStrLn "hi")
  x
  throwIO (userError "B")

The following cases throw B:

main = do
  evaluate (('a' : error "A") `deepseq` return () :: IO ())
  throwIO (userError "B")

main = do
  evaluate (('a' : error "A") `deepseq` putStrLn "hi")
  throwIO (userError "B")

main = do
  evaluate (('a' : error "A") `deepseq` getLine)
  throwIO (userError "B")


_______________________________________________
Glasgow-haskell-users mailing list
Glasgow-haskell-users <at> haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

_______________________________________________
Glasgow-haskell-users mailing list
Glasgow-haskell-users <at> haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Albert Y. C. Lai | 8 Nov 19:04 2012
Picon

Re: Using DeepSeq for exception ordering

On 12-11-08 01:01 PM, Nicolas Frisby wrote:
> And the important observation is: all of them throw A if interpreted in
> ghci or compiled without -O, right?

Yes.
Edward Z. Yang | 8 Nov 20:16 2012
Picon

Re: Using DeepSeq for exception ordering

It looks like the optimizer is getting confused when the value being
evaluated is an IO action (nota bene: 'evaluate m' where m :: IO a
is pretty odd, as far as things go). File a bug?

Cheers,
Edward

Excerpts from Albert Y. C. Lai's message of Thu Nov 08 10:04:15 -0800 2012:
> On 12-11-08 01:01 PM, Nicolas Frisby wrote:
> > And the important observation is: all of them throw A if interpreted in
> > ghci or compiled without -O, right?
> 
> Yes.
> 
Simon Marlow | 12 Nov 17:34 2012
Picon

Re: Using DeepSeq for exception ordering

Did you try -fpedantic-bottoms?

Cheers,
	Simon

On 08/11/2012 19:16, Edward Z. Yang wrote:
> It looks like the optimizer is getting confused when the value being
> evaluated is an IO action (nota bene: 'evaluate m' where m :: IO a
> is pretty odd, as far as things go). File a bug?
>
> Cheers,
> Edward
>
> Excerpts from Albert Y. C. Lai's message of Thu Nov 08 10:04:15 -0800 2012:
>> On 12-11-08 01:01 PM, Nicolas Frisby wrote:
>>> And the important observation is: all of them throw A if interpreted in
>>> ghci or compiled without -O, right?
>>
>> Yes.
>>
>
> _______________________________________________
> Glasgow-haskell-users mailing list
> Glasgow-haskell-users <at> haskell.org
> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
>
Albert Y. C. Lai | 12 Nov 17:54 2012
Picon

Re: Using DeepSeq for exception ordering

On 12-11-12 11:34 AM, Simon Marlow wrote:
> Did you try -fpedantic-bottoms?

Interesting option. And furthermore its doc refers to -fno-state-hack, too.

import Control.DeepSeq
import Control.Exception

main = do
   evaluate (('a' : error "A") `deepseq` putStrLn "hi")
   throwIO (userError "B")

-O -fpedantic-bottoms => B
-O -fno-state-hack => A
-O both => A

7.4.2, linux 32-bit x86
Simon Hengel | 12 Nov 17:56 2012
Picon

Re: Using DeepSeq for exception ordering

> Did you try -fpedantic-bottoms?

I just tried.  The exception (or seq?) is still optimized away.

Here is what I tried:

    -- file Foo.hs
    import Control.Exception
    import Control.DeepSeq
    main = evaluate (('a' : undefined) `deepseq` return () :: IO ())

    $ ghc -fforce-recomp -fpedantic-bottoms -O Foo.hs && ./Foo && echo bar
    [1 of 1] Compiling Main             ( Foo.hs, Foo.o )
    Linking Foo ...
    bar

Cheers,
Simon
Simon Marlow | 13 Nov 13:35 2012
Picon

Re: Using DeepSeq for exception ordering

On 12/11/2012 16:56, Simon Hengel wrote:
>> Did you try -fpedantic-bottoms?
>
> I just tried.  The exception (or seq?) is still optimized away.
>
> Here is what I tried:
>
>      -- file Foo.hs
>      import Control.Exception
>      import Control.DeepSeq
>      main = evaluate (('a' : undefined) `deepseq` return () :: IO ())
>
>      $ ghc -fforce-recomp -fpedantic-bottoms -O Foo.hs && ./Foo && echo bar
>      [1 of 1] Compiling Main             ( Foo.hs, Foo.o )
>      Linking Foo ...
>      bar

Sounds like a bug, -fpedantic-bottoms should work here.  Please open a 
ticket.

Cheers,
	Simon
Simon Hengel | 13 Nov 14:40 2012
Picon

Re: Using DeepSeq for exception ordering

> Sounds like a bug, -fpedantic-bottoms should work here.  Please open a
> ticket.

done [1].

[1] http://hackage.haskell.org/trac/ghc/ticket/7411
Antoine Latter | 9 Nov 05:54 2012
Picon

Re: Using DeepSeq for exception ordering

Is this related to imprecise exceptions?

http://research.microsoft.com/en-us/um/people/simonpj/papers/imprecise-exn.htm

Antoine



On Thu, Nov 8, 2012 at 12:01 PM, Nicolas Frisby <nicolas.frisby <at> gmail.com> wrote:
And the important observation is: all of them throw A if interpreted in ghci or compiled without -O, right?


On Thu, Nov 8, 2012 at 11:24 AM, Albert Y. C. Lai <trebla <at> vex.net> wrote:
On 12-11-08 07:12 AM, Simon Hengel wrote:
I was just going to say that I can give at least one counterexample
where this does not hold:

   evaluate (('a' : undefined) `deepseq` return () :: IO ())
   throwIO exceptionB

But then I realized that here exceptionA is optimized away altogether.
For me this smells like a bug.  Is this related to [1]?

[1] http://hackage.haskell.org/trac/ghc/ticket/2273

Interesting. A few more tests (all GHC 7.4.2, linux, x86 32-bit, use "ghc -O" to compile):

The following cases throw A:

import Control.DeepSeq
import Control.Exception

main = do
  evaluate (('a' : error "A") `deepseq` return () :: Maybe ())
  throwIO (userError "B")

main = do
  evaluate (('a' : error "A") `deepseq` ())
  throwIO (userError "B")

main = do
  evaluate (('a' : error "A") `deepseq` True)
  throwIO (userError "B")

main = do
  x <- evaluate (('a' : error "A") `deepseq` putStrLn "hi")
  x
  throwIO (userError "B")

The following cases throw B:

main = do
  evaluate (('a' : error "A") `deepseq` return () :: IO ())
  throwIO (userError "B")

main = do
  evaluate (('a' : error "A") `deepseq` putStrLn "hi")
  throwIO (userError "B")

main = do
  evaluate (('a' : error "A") `deepseq` getLine)
  throwIO (userError "B")


_______________________________________________
Glasgow-haskell-users mailing list
Glasgow-haskell-users <at> haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users


_______________________________________________
Glasgow-haskell-users mailing list
Glasgow-haskell-users <at> haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users


_______________________________________________
Glasgow-haskell-users mailing list
Glasgow-haskell-users <at> haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

Gmane