
Personally I'm not completely happy with the design, the IOError==Exception thing is a bit strange. But most of the complication arises if you try to mix the two interfaces to exceptions (IO and Exception) - if you stick to the Exception interface then the design is quite consistent.
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 together that you ask what IOError is identified with. But even without the H98 stuff,
ioErrors :: Exception -> Maybe IOError
should really be
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, the abuse of the types is clear. In the Prelude, we have
ioError :: IOError -> IO a userError :: String -> IOError catch :: IO a -> (IOError -> IO a) -> IO a
but userError produces only IOExceptions, and Prelude.catch 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 = Exception is that you can generalize ioError to all exceptions, despite its name. With IOError = IOException, you would have to add to Control.Exception
throwIO :: Exception -> IO a
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
isAlreadyExistsError :: IOError -> Bool ...
ioeGetErrorString :: IOError -> String ...
With IOError = Exception, these functions give runtime errors on anything that isn't actually an IOException. There is also (in IO and System.IO)
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
which again handle only IOExceptions, so there are new 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 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