为什么异常没有被 `catch` 捕获?
Why exception is not catching by `catch`?
为什么不捕获转换异常 conv B3
?!
import qualified Control.Monad.Catch as E
data A = A1|A2 deriving (Enum, Show)
data B = B1|B2|B3 deriving (Enum, Show)
conv b = safeConv
where
catchError e = Left e
safeConv = (Right $ (toEnum $ fromEnum b :: A)) `E.catch` catchError
我得到了:
Right *** Exception: toEnum{A}: tag (2) is outside of enumeration's range (0,1)
CallStack (from HasCallStack):
error, called at xxx.hs:227:26 in main:Main
Haskell 异常与 Java 或 C++ 有所不同:"true" 异常在 IO
monad 中工作,然后通过以下方式模仿异常纯粹的意思,如 ExceptT
.
toEnum
函数抛出第一种 - IO
异常, - 不能用纯代码捕获。他们飞到最近的 IO
地方,在你的情况下显然是 GHCi。
为了捕获此类异常,首先需要通过Control.Exception.evaluate
. Then you can catch such exceptions with catch
, or, if you just want to convert it to an Either exception A
(as you seem to be doing), there is an app for that! - it's called try
.
将抛出表达式包裹在IO
中
此外,当使用 catch
或 try
时,您需要指定要捕获的异常的特定 类型 。但是 it is possible to catch all exceptions 无论类型如何,都使用存在类型 SomeException
.
所以,总结所有这些,我们得到这个代码:
import qualified Control.Exception as E
data A = A1|A2 deriving (Enum, Show)
data B = B1|B2|B3 deriving (Enum, Show)
conv :: Enum b => b -> IO (Either E.SomeException A)
conv b = E.try . E.evaluate . toEnum $ fromEnum b
注意 1:conv
上的类型注释是必要的,以便将 E.SomeException
指定为要捕获的异常类型。没有它,GHC 会抱怨异常类型不明确。
NOTE 2:因为我们在conv
上的类型注解已经指定了目标类型A
,所以在toEnum $ fromEnum b
上的类型注解是no需要更长的时间。
注意 3:我已将您导入的 Control.Monad.Catch
替换为 Control.Exception
,因为那是 evaluate
和 SomeException
的位置是。
如果有人需要解决方案,我会把它留在这里。为了保持函数的纯净,转换应该是:
unsafeConvEnum :: (Enum a, Enum b) => a -> b
unsafeConvEnum = toEnum . fromEnum
convEnum :: (Enum a, Enum b) => a -> Maybe b
convEnum e = unsafePerformIO conv'
where onError (_::SomeException) = pure Nothing
conv' = (Just <$> evaluate (unsafeConvEnum e)) `catch` onError
没有任何 IO :)
为什么不捕获转换异常 conv B3
?!
import qualified Control.Monad.Catch as E
data A = A1|A2 deriving (Enum, Show)
data B = B1|B2|B3 deriving (Enum, Show)
conv b = safeConv
where
catchError e = Left e
safeConv = (Right $ (toEnum $ fromEnum b :: A)) `E.catch` catchError
我得到了:
Right *** Exception: toEnum{A}: tag (2) is outside of enumeration's range (0,1)
CallStack (from HasCallStack):
error, called at xxx.hs:227:26 in main:Main
Haskell 异常与 Java 或 C++ 有所不同:"true" 异常在 IO
monad 中工作,然后通过以下方式模仿异常纯粹的意思,如 ExceptT
.
toEnum
函数抛出第一种 - IO
异常, - 不能用纯代码捕获。他们飞到最近的 IO
地方,在你的情况下显然是 GHCi。
为了捕获此类异常,首先需要通过Control.Exception.evaluate
. Then you can catch such exceptions with catch
, or, if you just want to convert it to an Either exception A
(as you seem to be doing), there is an app for that! - it's called try
.
IO
中
此外,当使用 catch
或 try
时,您需要指定要捕获的异常的特定 类型 。但是 it is possible to catch all exceptions 无论类型如何,都使用存在类型 SomeException
.
所以,总结所有这些,我们得到这个代码:
import qualified Control.Exception as E
data A = A1|A2 deriving (Enum, Show)
data B = B1|B2|B3 deriving (Enum, Show)
conv :: Enum b => b -> IO (Either E.SomeException A)
conv b = E.try . E.evaluate . toEnum $ fromEnum b
注意 1:conv
上的类型注释是必要的,以便将 E.SomeException
指定为要捕获的异常类型。没有它,GHC 会抱怨异常类型不明确。
NOTE 2:因为我们在conv
上的类型注解已经指定了目标类型A
,所以在toEnum $ fromEnum b
上的类型注解是no需要更长的时间。
注意 3:我已将您导入的 Control.Monad.Catch
替换为 Control.Exception
,因为那是 evaluate
和 SomeException
的位置是。
如果有人需要解决方案,我会把它留在这里。为了保持函数的纯净,转换应该是:
unsafeConvEnum :: (Enum a, Enum b) => a -> b
unsafeConvEnum = toEnum . fromEnum
convEnum :: (Enum a, Enum b) => a -> Maybe b
convEnum e = unsafePerformIO conv'
where onError (_::SomeException) = pure Nothing
conv' = (Just <$> evaluate (unsafeConvEnum e)) `catch` onError
没有任何 IO :)