Dealing with different error types in `ExceptT` computation

93 Views Asked by At

(This question is similar to this question but that question only received a comment recommending the errors package, I would like some more details.)

I'm working on a program that uses two different packages that both return types IO (Either e a) or Either e a. However the e is not the same between the libraries.

I wonder how to structure functions that use both libraries. The hint that I should use errors led me to hush and note, but this doesn't feel quite right:

data MyError = Error1 | Error2 | Error3

f :: IO (Either MyErrorType Text)
f = do
  now <- someIoAction

  runExceptT $ do
    x <- note Error1 $ hush LibraryA.f
    y <- ExceptT $ note Error2 . hush <$> LibraryB.IO.f
    z <- ExceptT $ note Error3 . hush <$> LibraryB.IO.g
    pure (x, y, z)

How should one structure code like this? Should I match on results and convert the error types? Are there something else in the errors package that I should know about? I will be really grateful for an answer to the (somewhat) specific question about how to structure code like the one above and general pointers to dealing with non-uniform errors in Haskell.

1

There are 1 best solutions below

0
On

If you want to handle the error only once, I'd transform IO (Either e a) into IO a with a utility function like this:

throwWhenLeft :: Exception e => IO (Either e a) -> IO a
throwWhenLeft io = either throwIO return =<< io

This looks dangerous at a glance, but it's useful in some (very common, IMO) cases:

  • You have to handle various Exception thrown/returned either by IO a or some Either e a.
  • What exception handlers' do is almost always same.