如何将 Either 转换为 MonadThrow

How to convert Either to MonadThrow

我有一个通过 Either 处理错误的函数:

funErrViaEither :: a -> Either SomeException b

我想在另一个应该更灵活的函数中使用这个函数 return MonadThrow m:

funErrViaThrow :: MonadThrow m => a -> m b
funErrViaThrow x =
    if x = someCondition
        then funErrViaEither
        else throwM (SomeException MyCustomException)

这不编译;类型检查器抱怨 funErrViaEither 的 return 类型与预期类型 m b 不匹配。我不明白为什么 - Either 有一个 MonadThrow 的实例,SomeException 作为 Left.

的类型

我哪里错了?将通过 Either 发出的错误信号转换为通过 MonadThrow 发出的错误信号的正确方法是什么?

虽然您不能将 funErrViaEither x :: Either SomeException b 直接用作一般 MonadThrow m => m b,但您可以使用模式匹配处理 Either,根据需要抛出或返回:

case funErrViaEither x of
  Left err -> throwM err
  Right y -> return y

但是,我认为您可能用 SomeException 过度包装了异常。当您从 Either SomeException 切换到 MonadThrow m 时,您更有可能想要剥离它,因此完整的类型检查示例如下所示:

import Control.Monad.Catch

data MyCustomException = NoNegatives | NoOdds deriving (Show)
instance Exception MyCustomException

funErrViaEither :: Int -> Either SomeException Int
funErrViaEither n | n < 0     = throwM NoNegatives  -- or Left (SomeException NoNegatives)
                  | otherwise = Right $ n `div` 2

funErrViaThrow :: MonadThrow m => Int -> m Int
funErrViaThrow x =
    if even x
        then case funErrViaEither x of
               Left (SomeException err) -> throwM err  -- peel off SomeException
               Right y -> return y
        else throwM NoOdds

main = do
  print =<< funErrViaThrow 6
  (print =<< funErrViaThrow 5)
    `catch` (\err -> putStrLn $ "caught: " ++ show (err :: MyCustomException))
  print =<< funErrViaThrow (-2)