为什么模式匹配失败没有被异常处理程序捕获?

Why a pattern-matching failure is not catching by the exception handler?

在这种情况下,为什么我的异常处理程序 excToStr 没有捕捉到模式匹配失败?

我有一个传入的 POST 请求的处理程序,在 Scotty Web framework:

的控制下
...
import qualified Web.Scotty as W
...

    W.post "/some/endpoint" $ excToStr "Cannot handle it!" $ do
        b <- W.body

        -- throwString "aaa" <--- THIS IS HANDLED FINE!

        Just x::Maybe SomeMyType <- pure (decode b) -- BUT NOT THIS PATTERN-MATCHING FAILURE!
        liftIO $ print $ show x
        W.text "OK"

其中 excToStr 是我的,看起来像:

...
import qualified Data.Text.Lazy as LT
...

excH :: (String -> String) -> ActionT LT.Text IO () -> ActionT LT.Text IO ()
excH mkErr m = catchAnyDeep m (W.text . cs . mkErr . show)

excToStr :: String -> ActionT LT.Text IO () -> ActionT LT.Text IO ()
excToStr errMsg = excH (\details -> errMsg <> " (" <> details <> ")")

catchAnyDeep 来自 safe-exceptions 图书馆。我也尝试了其他功能(catchAnyhandlecatch 等)- 但没有成功。问题的症结在于,当传入的正文无法成功解码时(并且 decode returns Nothing 而不是 Just x),则模式匹配失败,所以我预计我的 extToStr(即 excH)会处理它,因为 catchAnyDeep(和 catchAny)处理 ANY 异常(包括模式匹配失败,对吗?):

catchAny :: MonadCatch m => m a -> (SomeException -> m a) -> m a

catchAnyDeep :: (MonadCatch m, MonadIO m, NFData a) => m a -> (SomeException -> m a) -> m a.

如果我用 throwString 抛出一个异常,那么它会按预期工作(异常被捕获)。但是模式匹配失败会导致 HTTP 内部错误 500,并显示消息“在 do 表达式中模式匹配失败......”。如何处理模式匹配异常?

scotty 操作(ActionT Text IO 类型)中有两种形式的异常。 IO 中有原生异常,ActionT 转换器添加了另一种形式。这些异常是单独处理的。接口由 the instances of ActionT:

给出
  • (MonadCatch m, ScottyError e) => MonadCatch (ActionT e m)(和 MonadThrow 实例类似)。这表明,当您使用 MonadCatch 中的 catch(或 MonadThrow 中的 throwString,以及 safe-exceptions[=44= 中的其他变体时] 库),你正在使用转换后的 monad m 的错误处理机制,在 Scotty 中通常是 IO(它定义 ActionM = ActionT Text IO)。

  • (Monad m, ScottyError e) => MonadFail (ActionT e m)MonadFail 是用于 do 块中部分 pattern-matches 的约束。它不需要来自底层 monad mMonadFail,这表明与 MonadThrow/MonadCatch 不同,它使用 ActionT 提供的异常机制变压器本身。要捕获此异常,您必须在 Scotty 中寻找组合器而不是辅助库,例如 rescue.