Network.firstSuccesful: 'throw' vs 'throwIO' usage

Hello, while reading over the source code of network[1], I noticed a use of 'throw' where I'd expect 'throwIO': import qualified Control.Exception as Exception catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a catchIO = Exception.catch -- Returns the first action from a list which does not throw an exception. -- If all the actions throw exceptions (and the list of actions is not empty), -- the last exception is thrown. firstSuccessful :: [IO a] -> IO a firstSuccessful [] = error "firstSuccessful: empty list" firstSuccessful (p:ps) = catchIO p $ \e -> case ps of [] -> Exception.throw e _ -> firstSuccessful ps ...so, is `throw` used properly in the code above, or should it rather be `throwIO`? [1]: http://hackage.haskell.org/packages/archive/network/2.3.1.0/doc/html/src/Net...

* Herbert Valerio Riedel
Hello,
while reading over the source code of network[1], I noticed a use of 'throw' where I'd expect 'throwIO':
import qualified Control.Exception as Exception
catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a catchIO = Exception.catch
-- Returns the first action from a list which does not throw an exception. -- If all the actions throw exceptions (and the list of actions is not empty), -- the last exception is thrown. firstSuccessful :: [IO a] -> IO a firstSuccessful [] = error "firstSuccessful: empty list" firstSuccessful (p:ps) = catchIO p $ \e -> case ps of [] -> Exception.throw e _ -> firstSuccessful ps
...so, is `throw` used properly in the code above, or should it rather be `throwIO`?
[1]: http://hackage.haskell.org/packages/archive/network/2.3.1.0/doc/html/src/Net...
In this particular situation it doesn't matter. If you use throwIO, then, if all actions fail, firstSuccesful will return a proper IO action which, when sequenced, throws an exception. If you use throw, then in the same situation the result of firstSuccessful will throw an exception before yielding a proper IO value. However, I agree with you that throwIO would be somewhat more idiomatic here. (And IIRC I wrote this code, so you can blame me.) -- Roman I. Cheplyaka :: http://ro-che.info/

On 06/09/2012 11:05, Roman Cheplyaka wrote:
* Herbert Valerio Riedel
[2012-09-06 11:40:23+0200] Hello,
while reading over the source code of network[1], I noticed a use of 'throw' where I'd expect 'throwIO':
import qualified Control.Exception as Exception
catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a catchIO = Exception.catch
-- Returns the first action from a list which does not throw an exception. -- If all the actions throw exceptions (and the list of actions is not empty), -- the last exception is thrown. firstSuccessful :: [IO a] -> IO a firstSuccessful [] = error "firstSuccessful: empty list" firstSuccessful (p:ps) = catchIO p $ \e -> case ps of [] -> Exception.throw e _ -> firstSuccessful ps
...so, is `throw` used properly in the code above, or should it rather be `throwIO`?
[1]: http://hackage.haskell.org/packages/archive/network/2.3.1.0/doc/html/src/Net...
In this particular situation it doesn't matter.
If you use throwIO, then, if all actions fail, firstSuccesful will return a proper IO action which, when sequenced, throws an exception.
If you use throw, then in the same situation the result of firstSuccessful will throw an exception before yielding a proper IO value.
However, I agree with you that throwIO would be somewhat more idiomatic here. (And IIRC I wrote this code, so you can blame me.)
Here is some background reading: http://hackage.haskell.org/trac/ghc/ticket/1171 The bottom line is that it's hard to tell what will happen if you use throw here. Always use throwIO if you can. Cheers, Simon

* Simon Marlow
On 06/09/2012 11:05, Roman Cheplyaka wrote:
* Herbert Valerio Riedel
[2012-09-06 11:40:23+0200] Hello,
while reading over the source code of network[1], I noticed a use of 'throw' where I'd expect 'throwIO':
import qualified Control.Exception as Exception
catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a catchIO = Exception.catch
-- Returns the first action from a list which does not throw an exception. -- If all the actions throw exceptions (and the list of actions is not empty), -- the last exception is thrown. firstSuccessful :: [IO a] -> IO a firstSuccessful [] = error "firstSuccessful: empty list" firstSuccessful (p:ps) = catchIO p $ \e -> case ps of [] -> Exception.throw e _ -> firstSuccessful ps
...so, is `throw` used properly in the code above, or should it rather be `throwIO`?
[1]: http://hackage.haskell.org/packages/archive/network/2.3.1.0/doc/html/src/Net...
In this particular situation it doesn't matter.
If you use throwIO, then, if all actions fail, firstSuccesful will return a proper IO action which, when sequenced, throws an exception.
If you use throw, then in the same situation the result of firstSuccessful will throw an exception before yielding a proper IO value.
However, I agree with you that throwIO would be somewhat more idiomatic here. (And IIRC I wrote this code, so you can blame me.)
Here is some background reading:
http://hackage.haskell.org/trac/ghc/ticket/1171
The bottom line is that it's hard to tell what will happen if you use throw here. Always use throwIO if you can.
So, regarding this example, does it mean that, under some circumstances, `firstSuccessful [a]` can throw `error "firstSuccessful: empty list"`? Would you also advise changing `error` to `throwIO . ErrorCall` here? -- Roman I. Cheplyaka :: http://ro-che.info/

On 06/09/2012 14:07, Roman Cheplyaka wrote:
* Simon Marlow
[2012-09-06 12:35:52+0100] On 06/09/2012 11:05, Roman Cheplyaka wrote:
* Herbert Valerio Riedel
[2012-09-06 11:40:23+0200] Hello,
while reading over the source code of network[1], I noticed a use of 'throw' where I'd expect 'throwIO':
import qualified Control.Exception as Exception
catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a catchIO = Exception.catch
-- Returns the first action from a list which does not throw an exception. -- If all the actions throw exceptions (and the list of actions is not empty), -- the last exception is thrown. firstSuccessful :: [IO a] -> IO a firstSuccessful [] = error "firstSuccessful: empty list" firstSuccessful (p:ps) = catchIO p $ \e -> case ps of [] -> Exception.throw e _ -> firstSuccessful ps
...so, is `throw` used properly in the code above, or should it rather be `throwIO`?
[1]: http://hackage.haskell.org/packages/archive/network/2.3.1.0/doc/html/src/Net...
In this particular situation it doesn't matter.
If you use throwIO, then, if all actions fail, firstSuccesful will return a proper IO action which, when sequenced, throws an exception.
If you use throw, then in the same situation the result of firstSuccessful will throw an exception before yielding a proper IO value.
However, I agree with you that throwIO would be somewhat more idiomatic here. (And IIRC I wrote this code, so you can blame me.)
Here is some background reading:
http://hackage.haskell.org/trac/ghc/ticket/1171
The bottom line is that it's hard to tell what will happen if you use throw here. Always use throwIO if you can.
So, regarding this example, does it mean that, under some circumstances, `firstSuccessful [a]` can throw `error "firstSuccessful: empty list"`?
I think this is the case with GHC as it stands, although it isn't clear whether we want that behaviour or not (see the ticket). It boils down to this case ps of [] -> throw e _ -> error "firstSuccessful..." Now suppose GHC floated out one of the case branches: let x = error "firstSuccessful..." case ps of [] -> throw e _ -> x and now the strictness analyser can prove that x is strict, because the case expression has value _|_. So it evaluates x early, and you get an unexpected exception. Cheers, Simon
Would you also advise changing `error` to `throwIO . ErrorCall` here?

* Simon Marlow
On 06/09/2012 14:07, Roman Cheplyaka wrote:
* Simon Marlow
[2012-09-06 12:35:52+0100] On 06/09/2012 11:05, Roman Cheplyaka wrote:
* Herbert Valerio Riedel
[2012-09-06 11:40:23+0200] Hello,
while reading over the source code of network[1], I noticed a use of 'throw' where I'd expect 'throwIO':
import qualified Control.Exception as Exception
catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a catchIO = Exception.catch
-- Returns the first action from a list which does not throw an exception. -- If all the actions throw exceptions (and the list of actions is not empty), -- the last exception is thrown. firstSuccessful :: [IO a] -> IO a firstSuccessful [] = error "firstSuccessful: empty list" firstSuccessful (p:ps) = catchIO p $ \e -> case ps of [] -> Exception.throw e _ -> firstSuccessful ps
...so, is `throw` used properly in the code above, or should it rather be `throwIO`?
[1]: http://hackage.haskell.org/packages/archive/network/2.3.1.0/doc/html/src/Net...
In this particular situation it doesn't matter.
If you use throwIO, then, if all actions fail, firstSuccesful will return a proper IO action which, when sequenced, throws an exception.
If you use throw, then in the same situation the result of firstSuccessful will throw an exception before yielding a proper IO value.
However, I agree with you that throwIO would be somewhat more idiomatic here. (And IIRC I wrote this code, so you can blame me.)
Here is some background reading:
http://hackage.haskell.org/trac/ghc/ticket/1171
The bottom line is that it's hard to tell what will happen if you use throw here. Always use throwIO if you can.
So, regarding this example, does it mean that, under some circumstances, `firstSuccessful [a]` can throw `error "firstSuccessful: empty list"`?
I think this is the case with GHC as it stands, although it isn't clear whether we want that behaviour or not (see the ticket). It boils down to this
case ps of [] -> throw e _ -> error "firstSuccessful..."
Now suppose GHC floated out one of the case branches:
let x = error "firstSuccessful..." case ps of [] -> throw e _ -> x
and now the strictness analyser can prove that x is strict, because the case expression has value _|_. So it evaluates x early, and you get an unexpected exception.
I see, thank you. I'll make a patch for the network library to fix this. Regarding my other question
Would you also advise changing `error` to `throwIO . ErrorCall` here?
... looks like this doesn't buy us anything? -- Roman I. Cheplyaka :: http://ro-che.info/
participants (3)
-
Herbert Valerio Riedel
-
Roman Cheplyaka
-
Simon Marlow