为什么异常处理程序无法捕获模式匹配失败?

问题描述

在这种情况下,为什么异常匹配处理程序excToStr无法捕获模式匹配失败?

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

...
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返回nothing而不是Just x),则模式匹配将失败,因此我希望我的{{1 }}(即extToStr)将处理它,因为excH(和catchAnyDeep)将处理 ANY 异常(包括模式匹配失败,对吧?): / p>

catchAny

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

如果我仅用catchAnyDeep :: (MonadCatch m,Monadio m,NFData a) => m a -> (SomeException -> m a) -> m a抛出一个异常,则它按预期方式工作(该异常被捕获)。但是模式匹配失败会导致HTTP内部错误500,并显示一条消息“ ...中do表达式中的模式匹配失败”。如何处理模式匹配异常?

解决方法

在侦查动作中(类型ActionT Text IO)有两种形式的异常。 IO中存在本机异常,而ActionT转换器添加了另一种形式。这些异常单独处理。该接口由the instances of ActionT给出:

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

  • (Monad m,ScottyError e) => MonadFail (ActionT e m)MonadFaildo块中用于部分模式匹配的约束。它不需要基础单子MonadFail的{​​{1}},这表明与m / MonadThrow不同,它使用了MonadCatch提供的异常机制变压器本身。要捕获此异常,您必须在Scotty中寻找组合器,而不是诸如rescue之类的辅助库。