Exceptions are convenient in that you can rely on libraries throwing them
instead of prechecking for valid values yourself (for instance, why check
that the argument to Char.digitToInt is valid if digitToInt does so already),
and you don't have to modify a lot of function signatures. Unfortunately, in
the face of lazy evaluation, they can get thrown in unexpected places. Even
with Exception.evaluate, or code which is already explicitly sequenced in the
IO monad, like "t <- Exception.evaluate (map Char.digitToInt ['a'..'g'])" an
exception will only be thrown when you inspect the last element of 't'.
(digitToInt confusingly accepts hex "digits"---shouldn't it be "higitToInt"
then?).
So it seems to me that if you are, say, checking input, the options are
to handle exceptions from the checking code but expect them to come from the
processing code, or decorate all checking code with Rights and Lefts.
Problems with the first option are that checking code could also trigger some
exceptions, and Prelude functions throw undescriptive errors like "user
error" or low level ones like "refuted pattern match" and catching them over
the entire program means you could stifle a lot of real errors. This implies
that you have to make specific exceptions and convert Prelude and library
exceptions into yours as low down as possible, which is cluttering but maybe
not as cluttering as Either. Problems with the second option are many of the
problems that lead to us wanting exceptions in the first place.
Using Either seems much simpler and functional-friendly. So then, in what
contexts are exceptions appropriate in haskell?