Exception 我的safeMapM赢了';我没有发现任何例外

Exception 我的safeMapM赢了';我没有发现任何例外,exception,haskell,Exception,Haskell,我正在尝试创建一个安全版本的mapM,如果执行时抛出异常,它将从结果中排除元素 safeMapM :: (a -> IO b) -> [a] -> IO [b] safeMapM f [] = return [] safeMapM f (x : xs) = do restResult <- safeMapM f xs appliedResult <- onException (f x >>= evaluate . Just)

我正在尝试创建一个安全版本的
mapM
,如果执行时抛出异常,它将从结果中排除元素

safeMapM :: (a -> IO b) -> [a] -> IO [b]
safeMapM f []       = return []
safeMapM f (x : xs) = do
    restResult <- safeMapM f xs
    appliedResult <- onException (f x >>= evaluate . Just) (return Nothing)
    case appliedResult of
        Just x' -> return $ x' : restResult
        Nothing -> return restResult
它失败于:

*** Exception: 3
[1,2,"ghci>" 

为什么它没有被抓住?
评估
是否评估得不够充分,无法捕获
错误
?有没有一种方法可以绕过这个问题,而不需要将签名更改为
safeMapM::(NFData a,NFData b)=>(a->IO b)->[a]->IO[b]
,并使用
deepseq

评估x
只评估
x
足够远的程度,以使其进入,而且
x
已经在WHNF中,即使
x
是一个完全未经评估的thunk。所以你有了
evaluate(只是x)
,它根本不涉及
x
的评估

而不是将
fx
绑定到函数
evaluate。Just
,您需要使用一个函数来计算
x
,然后再将其包装为
Just
,如下所示:

returnEval :: Monad m => a -> IO (m a)
returnEval x = evaluate x >>= return . return
使用该函数,您可以像这样重写求值行:

appliedResult <- onException (f x >>= returnEval) (return Nothing)

但正如John L在评论中所说,这仍然只捕获最高层的异常:进入WHNF所必需的异常。如果您真的想强制对整个嵌套表达式进行急切求值(您可能不想这样做),那么您需要一个更直截了当的工具,例如deepseq。

evaluate
仅计算WHNF。在本例中,这就是在调用
evaluate
之前添加的
包装器。删除
只是
并相应地修改您的函数,它将捕获此错误。但是,如果不使用
deepseq
,它仍然无法捕获所有内容。这就是我避免
error
和其他异步异常的原因:它们更难捕获。
appliedResult <- onException (f x >>= returnEval) (return Nothing)
module SafeEval where
import Control.Exception

returnEval :: Monad m => a -> IO (m a)
returnEval x = evaluate x >>= return . return

safeMapM :: (a -> IO b) -> [a] -> IO [b]
safeMapM f []       = return []
safeMapM f (x : xs) = do
    restResult <- safeMapM f xs
    appliedResult <- catch (f x >>= returnEval) (\e -> (e :: SomeException) `seq` return Nothing)
    case appliedResult of
        Just x' -> return $ x' : restResult
        Nothing -> return restResult