Почему ошибка сопоставления с образцом не улавливается обработчиком исключений?

#haskell #exception #scotty

#haskell #исключение #скотти

Вопрос:

Почему ошибка сопоставления с образцом не улавливается моим обработчиком исключений 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 из библиотеки безопасных исключений. Я также пробовал другие функции ( catchAny , handle , catch , и т. Д.) — Без успеха. Суть проблемы в том, что когда входящее тело не может быть успешно декодировано (и decode возвращается Nothing вместо Just x ), тогда сопоставление с образцом завершается неудачно, поэтому я ожидаю, что мой extToStr (т.е. excH ) обработает это, потому catchAnyDeep что (и 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 .

Если я создаю только исключение, throwString то оно работает так, как ожидалось (исключение перехватывается). Но ошибка сопоставления с образцом приводит к внутренней ошибке HTTP 500 с сообщением «Ошибка сопоставления с образцом в выражении do в ….». Как также обрабатывать исключения сопоставления с образцом?

Ответ №1:

В действии Скотти (типа ) есть две формы исключений ActionT Text IO . Есть собственные исключения в IO , и другая форма, добавленная ActionT преобразователем. Эти исключения обрабатываются отдельно. Интерфейс задается экземплярами ActionT :

  • (MonadCatch m, ScottyError e) => MonadCatch (ActionT e m) (и аналогично MonadThrow экземпляру). Это показывает, что когда вы используете catch from MonadCatch (или throwString from MonadThrow и другие варианты из библиотеки безопасных исключений), вы используете механизм обработки ошибок преобразованной монады m , который в Scotty обычно IO (он определяет ActionM = ActionT Text IO ).

  • (Monad m, ScottyError e) => MonadFail (ActionT e m) . MonadFail используется ли ограничение для частичных совпадений с образцом в do блоках. Он не требует a MonadFail от базовой монады m , что указывает на то, что, в отличие MonadThrow от / MonadCatch , он использует механизм исключения, предоставляемый самим ActionT преобразователем. Чтобы перехватить это исключение, вы должны искать комбинатор в Scotty, а не вспомогательную библиотеку, такую как rescue .