I'm using the EitherT monad transformer. Combining it with the IO monad, I'm afraid I would get an exception and it would not be caught.
Indeed the exception just passes through:
import Control.Monad.Trans
import Control.Error
import System.Directory
main = runEitherT testEx >>= print
testEx :: EitherT String IO ()
testEx = lift $ removeFile "non existing filename"
But the EitherT
otherwise fits the bill perfectly to convey to callers the error. So I want to use that, not throw exceptions...
I looked at try
from Control.Exception:
try :: Exception e => IO a -> IO (Either e a)
It looks to be exactly what I want, it would fit in my EitherT IO stack... (probably with an added hoistEither
and maybe fmapL
and it starts looking verbose though) But a naive lift $ try
doesn't typecheck.
I'm sure this problem has been solved thousands of times, but I can't find any good link describing this exact issue. How is this supposed to be solved?
EDIT By "how is this supposed to be solved", I was interested in the idiomatic solution, what would be the standard way to handle that in haskell. From the answers so far, it seems the idiomatic way is to let the exceptions be thown and handle them higher-up. Seems like a bit counter-intuitive to have two flows of control and return paths, but it is apparently the way it's meant to be done.
lifted-base
seems to be what you're looking for, which I found thanks to this post. – bheklilr