Haskell "исключения"
У меня есть набор пользователей, групп и сопоставление между пользователями и группами. У меня есть различные функции, которые управляют этими наборами, однако нельзя добавлять сопоставление групп пользователей ↔ для пользователя, который не существует, и не удалять группу, которая по-прежнему имеет пользователей в качестве членов и т.д.
Поэтому в основном я хочу, чтобы эти функции выдавали "исключения", которые должны быть явно обработаны вызывающим.
Я сначала подумал о возвращении чего-то вроде этого:
data Return r e = Success r | Exception e
И если вызывающий абонент не сможет сопоставить шаблон с Exception
, они, надеюсь, получат предупреждение о компиляторе или, по крайней мере, имеют очевидную ошибку времени выполнения при возникновении проблемы.
Это лучший подход, и есть ли это готовое решение? Примечание. Мне нужно перехватывать "исключения" в чистом коде, а не в IO Monad.
Ответы
Ответ 1
Да, это хороший подход, и он находится в стандартной библиотеке: Return r e
совпадает с Either e r
. Вы даже можете писать код, как если бы вы использовали исключения в IO
(тоже, без необходимости явно обрабатывать ошибки на каждом шаге с помощью сопоставления с образцом): экземпляр Monad
для Either
распространяется на ошибки, как и на Maybe
monad делает (но с дополнительным значением e
в случае ошибки). Например:
data MyError
= Oops String
| VeryBadError Int Int
mightFail :: T -> Either MyError Int
mightFail a = ...
foo :: T -> T -> Int -> Either MyError Int
foo a b c = do
x <- mightFail a
y <- mightFail b
if x == y
then throwError (VeryBadError x y)
else return (x + y + c)
Если mightFail a
или mightFail b
возвращает Left someError
, то foo a b c
тоже будет; ошибки автоматически распространяются. (Здесь throwError
является просто хорошим способом записи Left
, используя функции из Control.Monad.Error
, а также catchError
, чтобы поймать эти исключения.)
Ответ 2
Тип Return r e
, который вы описываете, является стандартным типом
data Either a b = Left a | Right b
Возможно, вы захотите использовать так называемую "monad ошибки" (более подходящим названием является "monad" исключения) пакета mtl. (Альтернативно, там ExceptionT
в пакете monadLib, если вы не хотите использовать mtl.) Это позволяет вам обрабатывать ошибки в чистом коде, вызывая throwError
и catchError
.
Здесь вы можете найти пример, который показывает, как его использовать.