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