Ошибка catchError на практике

#haskell #error-handling

#haskell #обработка ошибок

Вопрос:

У меня очень типичная настройка с набором функций в монаде ввода-вывода, которые могут выдавать ошибки. На сегодняшний день я только что имел дело с ошибками в конце цепочки монад с помощью шаблона, соответствующего любому результату runErrorT:

 replLisp :: LispScope -> String -> IO String
replLisp s input = do
  result <- runErrorT (evalLisp s input)
  return $ either (id) (show) result 
  

Теперь я хотел бы добавить некоторую обработку ошибок в мою взломанную маленькую схему, но у меня возникли проблемы с обеспечением работоспособности средства проверки типов.

Как использовать catchError? Один или два примера были бы полезны.


Это моя последняя попытка:

 catch :: [LispVal] -> IOThrowsError LispVal
catch [action rescue] = do
  eval action >>= catchError $ eval rescue
  

Ответ №1:

Вот пример использования catchError для восстановления после предыдущего вызова throwError :

 import Control.Monad.Error
import Control.Monad.Identity

type MyMonad = ErrorT String Identity
runMyMonad = runIdentity . runErrorT

main = do
        let x = runMyMonad (func 5 0)
        print x

func :: Double -> Double -> MyMonad Double
func w x = do
        y <- (divider x) `catchError` (_ -> return 1)
        return (w   y)

divider :: Double -> MyMonad Double
divider x = do
        when (x == 0) (throwError "Can not divide by zero!")
        return (10 / x)
  

Несмотря на передачу 0 для разделения, мы можем завершить с результатом обработчика 1 для получения выходных данных Right 6.0 .

Помогает ли это? В вашем вопросе на самом деле не было сказано, в чем проблема.

Ответ №2:

Монады ошибок любят либо то, либо другое и, возможно, не позволяют вам наблюдать ошибку из одной и той же монады: вы должны запустить монаду, чтобы наблюдать ее. Исключения в IO — это одно заметное исключение (кхм), потому что IO — это конец строки… вы не можете идти дальше оттуда.

У вас есть несколько возможностей:

  • Поскольку вы пишете мини-интерпретатор, вероятно, хорошей идеей будет явно управлять всеми исключениями, используя монаду ErrorT только для истинных, неустранимых ошибок.

  • Для любого вызова, который может привести к ошибке, от которой вы хотите иметь возможность восстановиться, выполните runErrorT и проверьте этот результат, прежде чем передавать результат в текущей монаде.

Комментарии:

1. Тогда какая функция catchError находится под контролем. Ошибка. Монада, используемая для?

2. Ах, упс, я случайно описал, как вы бы реализовали catchError ^_^ (Я явно не использовал Control. Ошибка. Достаточно монады …)

3. Обратите внимание, что (AFAIK) catchError удерживает вас в монадическом контексте (даже если ошибка была обнаружена), тогда как runExceptT и др. выходят из контекста и возвращают простое Either значение.