Why pattern-matching failure is not catching by my exception handler excToStr in this case?
I have a handler of an incoming POST request as under the control of the 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"
where excToStr is mine and it looks like:
...
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 is from safe-exceptions library. I tried another functions as well (catchAny, handle, catch, etc) - without success. The crux of the problem is that when incoming body cannot be decoded successfully (and decode returns Nothing instead of Just x), then pattern-match fails, so I expect that my extToStr (ie. excH) will handle it because catchAnyDeep (and catchAny) handles ANY exception (including pattern-match failures, right?):
catchAny :: MonadCatch m => m a -> (SomeException -> m a) -> m a
and
catchAnyDeep :: (MonadCatch m, MonadIO m, NFData a) => m a -> (SomeException -> m a) -> m a.
If I throw just an exception with throwString then it works as expected (the exception gets caught). But the pattern matching failure leads to an HTTP internal error 500 with a message "Pattern match failure in do expression at....". How to handle pattern-matching exceptions as well?
There are two forms of exceptions in a scotty action (of type
ActionT Text IO). There are native exceptions inIO, and another form added by theActionTtransformer. These exceptions are handled separately. The interface is given by the instances ofActionT:(MonadCatch m, ScottyError e) => MonadCatch (ActionT e m)(and similarly theMonadThrowinstance). This shows that when you usecatchfromMonadCatch(orthrowStringfromMonadThrow, and other variants from the safe-exceptions library), you are using the error handling mechanism of the transformed monadm, which in Scotty is usuallyIO(it definesActionM = ActionT Text IO).(Monad m, ScottyError e) => MonadFail (ActionT e m).MonadFailis the constraint used for partial pattern-matches indoblocks. It does not require aMonadFailfrom the underlying monadm, which indicates that, unlikeMonadThrow/MonadCatch, it uses the exception mechanism provided by theActionTtransformer itself. To catch this exception you must look for a combinator in Scotty rather than an auxiliary library, such asrescue.