问题
I have a function that handles errors via Either
:
funErrViaEither :: a -> Either SomeException b
I want to use this function in another function that should be more flexible and return MonadThrow m
:
funErrViaThrow :: MonadThrow m => a -> m b
funErrViaThrow x =
if x = someCondition
then funErrViaEither
else throwM (SomeException MyCustomException)
This does not compile; the type checker complains that the return type of funErrViaEither
does not match the expected type m b
. I don't understand why - Either
has an instance of MonadThrow
with SomeException
as the type of Left
.
Where do I err? What would be the correct way to convert an error signalled via Either
into one signalled via MonadThrow
?
回答1:
While you can't use funErrViaEither x :: Either SomeException b
directly as a general MonadThrow m => m b
, you can process the Either
using pattern matching, throwing or returning as appropriate:
case funErrViaEither x of
Left err -> throwM err
Right y -> return y
However, I think you've probably over-wrapped your exceptions with SomeException
. It's more likely that you want to peel this off when you switch from Either SomeException
to MonadThrow m
, so a full type-checked example would look like:
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)
来源:https://stackoverflow.com/questions/62362053/how-to-convert-either-to-monadthrow