2014-01-07 3 views
1

Можно ли дать выражение типа@djinn MonadError е т => т()

MonadError e m => m() 

, что вызывает ошибку, которая может быть обработана с catchError? Обратите внимание на отсутствие требования Error e.

При каких обстоятельствах может возникнуть ошибка с fail с catchError? Что может привести к тому, что fail не будет улавливаться catchError? Как изменились эти обстоятельства в отношении выпусков GHC? (Существует mention, что поведение изменилось в базе 4.3.)

фона этого вопроса больший кусок кода (Igor2 функция nomatch) где ошибка создается с fail "some message" проходов по catchError и завершает программу, но с заменой вызов fail с throwError undefined приводит к тому, что ошибка ломается, как ожидалось. Конечно, throwError undefined является уродливым взломом, и этот вопрос направлен на понимание фона и правильное решение.

ответ

3

Здесь есть несколько отдельных вопросов, поэтому я пройду через них в порядке.

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 экземпляра).

+0

Можете ли вы также построить небольшой пример, когда 'fail' проходит мимо' catchError'? –

+1

@ Helmut: Добавлен простой пример. – shang

+0

Большое спасибо за этот всеобъемлющий ответ, который позволил мне заставить Igor2 работать намного лучше с GHC 7. Я заменил эти сбои, которые нужно поймать с помощью 'throwError. strMsg' и в случае необходимости ввел требование «Ошибка». К счастью, все пользователи соответствующих функций уже предоставили дополнительный экземпляр. –

Смежные вопросы