#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
значение.