IOError vs. Exception vs. IOException

Simon Marlow simonmar@microsoft.com
Fri, 1 Nov 2002 12:29:32 -0000


> > Personally I'm not completely happy with the design, the
> > IOError=3D=3DException thing is a bit strange.  But most of the=20
> complication
> > arises if you try to mix the two interfaces to exceptions (IO and
> > Exception) - if you stick to the Exception interface then=20
> the design is
> > quite consistent.
>=20
> Well that's true in the sense that Exception and IOException come from
> the Control.Exception interface and IOError comes from the Haskell 98
> Prelude+IO interface, and it's only when you use them=20
> together that you
> ask what IOError is identified with.  But even without the H98 stuff,
>=20
> 	ioErrors :: Exception -> Maybe IOError
>=20
> should really be
>=20
> 	ioErrors :: Exception -> Maybe IOException

Agreed.

> and it's wierd that the function to throw general exceptions in the IO
> monad is called ioError.  When you bring in the H98 stuff,=20
> the abuse of
> the types is clear.  In the Prelude, we have
>=20
> 	ioError    :: IOError -> IO a
> 	userError  :: String -> IOError
> 	catch      :: IO a -> (IOError -> IO a) -> IO a
>=20
> but userError produces only IOExceptions, and Prelude.catch=20
> catches only IOExceptions.

userError isn't really useful when you have the full Exception
interface: it's just like error but in the IO monad (ie.
evaluate.error).

> (Having the same type as Control.Exception.catch is a bug,
> not a feature.)  The only gain from identifying IOError =3D Exception =
is
> that you can generalize ioError to all exceptions, despite its name.
> With IOError =3D IOException, you would have to add to =
Control.Exception
>=20
> 	throwIO :: Exception -> IO a
>=20
> as suggested by Alastair a while ago.

FWIW, I agree with you, and I don't have any objections to changing it
(but Simon P.J. might).

>  In IO (and System.IO.Error) we have
>=20
> 	isAlreadyExistsError  :: IOError -> Bool
> 	...
>=20
> 	ioeGetErrorString     :: IOError -> String
> 	...
>=20
> With IOError =3D Exception, these functions give runtime errors=20
> on anything
> that isn't actually an IOException.  There is also (in IO and=20
> System.IO)
>=20
> 	try        :: IO a -> IO (Either IOError a)
> 	bracket    :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
> 	bracket_   :: IO a -> (a -> IO b) -> IO c -> IO c
>=20
> which again handle only IOExceptions, so there are new=20
> versions of these
> three in Control.Exception.  It seems that the old bracket functions
> should now never be used, unless you know the whole program=20
> will be H98.

For bracket and bracket_, I agree.  For try, we've found that
Exception.try is very rarely what you actually want: in general it's
wrong to catch *all* exceptions unless you plan to re-throw the ones
that you don't care about.  So IO.try is still useful, but as you say
it's equivalent to tryJust ioErrors (and the latter is more readable).

> I would advocate moving them to haskell98/IO.hs, so users of the new
> libraries don't have to hide them.

I think that's a good idea, regardless of what we do about the rest of
the interface.

Cheers,
	Simon