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

Roman Cheplyaka roma at ro-che.info
Thu Sep 6 15:34:21 CEST 2012


* Simon Marlow <marlowsd at gmail.com> [2012-09-06 14:21:46+0100]
> On 06/09/2012 14:07, Roman Cheplyaka wrote:
> >* Simon Marlow <marlowsd at gmail.com> [2012-09-06 12:35:52+0100]
> >>On 06/09/2012 11:05, Roman Cheplyaka wrote:
> >>>* Herbert Valerio Riedel <hvr at gnu.org> [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/Network.html#firstSuccessful
> >>>
> >>>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/



More information about the Libraries mailing list