直接使用if
您可以直接以Ingo beautifully encapsulated 执行此操作,或者等效地例如
breakOut :: a -> m (Either MyErrorType MyGoodResultType)
breakOut x = do
y <- dosomethingWith x
z <- doSomethingElseWith x y
if isNoGood z then return (Left (someerror z)) else do
w <- process z
v <- munge x y z
u <- fiddleWith w v
return (Right (greatResultsFrom u z))
这很适合根据你的价值观做一些不同的事情。
在 IO monad 中使用异常
您可以将Control.Exception 用作Michael Litchard correctly pointed out。它有大量的错误处理、控制流改变的东西,如果你想用它做一些复杂的事情,值得一读。
如果您的错误产生可能发生在任何地方并且您的代码很复杂,那么这非常好。您可以在顶层或您喜欢的任何级别处理错误。它非常灵活,不会与您的返回类型混淆。它只适用于 IO monad。
import Control.Exception
真的我应该推出我自己的自定义类型,但我不会为派生 Typable 等而烦恼,所以我将使用标准的 error 函数和一些字符串来破解它。我对此感到非常内疚。
handleError :: ErrorCall -> IO Int
handleError (ErrorCall msg) = case msg of
"TooBig" -> putStrLn "Error: argument was too big" >> return 10000
"TooSmall" -> putStrLn "Error: argument was too big" >> return 1
"Negative" -> putStrLn "Error: argument was too big" >> return (-1)
"Weird" -> putStrLn "Error: erm, dunno what happened there, sorry." >> return 0
错误处理程序需要在catch 中使用显式类型。我已经 flipped 参数让 do 块排在最后。
exceptOut :: IO Int
exceptOut = flip catch handleError $ do
x <- readLn
if (x < 5) then error "TooSmall" else return ()
y <- readLn
return (50 + x + y)
Monad 转换器等
这些设计用于任何 monad,而不仅仅是 IO。它们与 IO 的异常具有相同的好处,因此在官方上很棒,但是您需要了解 monad 转换器。如果您的 monad 不是 IO,并且您有复杂的要求(如我对 Control.Exception 所说),请使用它们。
首先,阅读Gabriel Conzalez's Breaking from a loop 了解使用EitherT 来根据出现的某些情况做两件不同的事情,或者阅读MaybeT 了解在出现问题时直接停在那里。
如果您对 Monad Transformers 一无所知,可以从Martin Grabmüller's Monad Transformers Step by Step 开始。它涵盖ErrorT。之后再次阅读 Breaking from a Loop!
您可能还想阅读Real World Haskell chapter 19, Error handling。
致电/抄送
Continuation Passing Style 的callCC 非常强大,但也可能过于强大,而且肯定不会产生非常易于理解的代码。请参阅this 以获得相当积极的看法,this 获得非常消极的看法。