Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

haskell - exceptions and monad transformers

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I actually think EitherT is not the right thing to do here. What you're trying to say is "IO is for side-effects, and EitherT is for exceptions." But that's not true: IO always has the potential to result in an exception, so all you're doing is adding a false sense of security to your API, and introducing two ways that exceptions can be thrown instead of one. In addition, instead of using the well structured SomeException favored by IO, you're reducing down to String, which throws away information.

Anyway, if you're convinced that this is what you want to do, it's not too difficult. It looks something like:

eres <- liftIO $ try x
case eres of
    Left e -> throwError $ show (e :: SomeException)
    Right x -> return x

Note, however, that this will also swallow up async exceptions, which is usually not what you want to do. I think a better approach for that is enclosed-exceptions.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...