Здесь есть несколько отдельных вопросов, поэтому я пройду через них в порядке.
MonadError e m => m()
Проблема с вышесказанным, что если e
не ограничен каким-либо образом, что невозможно создать значение типа e
, которые вам нужно для throwError
. Так что нет, единственное допустимое значение для указанного выше типа, который вызывает ошибку нужно будет включить
throwError undefined
который является отговоркой (и не очень полезно). Ограничение Error e
позволяет оставить абзац e
, предоставив механизм для построения значения типа e
из строки.
В каких обстоятельствах может возникнуть ошибка, возникшая с ошибкой, с помощью catchError? Что может привести к сбою, не будучи пойманным catchError?
Это полностью зависит от рассматриваемой монады. Различные экземпляры MonadError
могут иметь различную реализацию для fail
, поэтому нет никакого общего ответа. Или, говоря иначе, нет никаких гарантий того, что ошибка, вызванная fail
, может обрабатываться catchError
, если вы не используете конкретный экземпляр MonadError
, который делает это обещание. Например:
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.Error
example :: (Error e, MonadError e m) => m String
example = fail "This is a failure" `catchError` const (return "Error was caught")
main = do
example >>= putStrLn
either putStrLn putStrLn example
В первой строке main
, example
использует MonadError
экземпляр IO
и вторая линия использует MonadError
экземпляр Either String
.fail
приводит к catchable ошибки в IO
, но не в Either
поэтому программа выводит
Error was caught
*** Exception: This is a failure
Однако, если заменить example
с
example :: (Error e, MonadError e m) => m String
example = throwError (strMsg "This is a failure")
`catchError` const (return "Error was caught")
Тогда мы получим требуемый выход
Error was caught
Error was caught
И это будет работать одинаково для любой (действительный) экземпляр o f MonadError
, в отличие fail
.
Каким образом эти обстоятельства изменились в отношении выпусков GHC? (Существует упоминание о том, что поведение изменилось в базе 4.3.)
Изменение основания 4.3 относится к тому, как fail
работы в Either
монады. В предыдущих версиях fail
возвращал значение Left
, которое могло быть уловлено с catchError
, но в 4.3 и после того, как оно использует error
и вызывает исключение, которое должно обрабатываться в IO
(например, catch
).
Проблема в Igor2 звучит, как он использует Either
монады и предполагает старое поведение fail
, когда он должен использовать throwError
(или просто Left
, если нет подходящего Error
экземпляра).
Можете ли вы также построить небольшой пример, когда 'fail' проходит мимо' catchError'? –
@ Helmut: Добавлен простой пример. – shang
Большое спасибо за этот всеобъемлющий ответ, который позволил мне заставить Igor2 работать намного лучше с GHC 7. Я заменил эти сбои, которые нужно поймать с помощью 'throwError. strMsg' и в случае необходимости ввел требование «Ошибка». К счастью, все пользователи соответствующих функций уже предоставили дополнительный экземпляр. –