2012-05-16 3 views
14

У меня есть набор пользователей, групп и сопоставление между пользователями и группами. У меня есть различные функции, которые управляют этими наборами, однако нельзя добавлять пользователя < -> сопоставление групп для пользователя, которого не существует, и не удалять группу, которая все еще имеет пользователей в качестве членов и т. Д.Haskell «exceptions»

в основном я хочу, чтобы эти функции выдавали «исключения», которые должны быть явно обработаны вызывающим.

Я сначала подумал о возвращении что-то вроде этого:

data Return r e = Success r | Exception e 

И если вызывающий абонент не шаблон матч против Exception, они будут надеяться получить предупреждение компилятора, или, по крайней мере, имеют очевидные выполнения ошибка при возникновении проблемы.

Это лучший подход, и есть ли готовое решение, которое это делает? Примечание. Мне нужно бросить и поймать «исключения» в чистом коде, а не в IO Monad.

ответ

19

Да, это хороший подход, и он находится в стандартной библиотеке: Return r e - это то же самое, что и Either e r. Вы даже можете писать код, как если бы вы использовали исключения в IO (т. Е. Без необходимости явно обрабатывать ошибки на каждом шаге с помощью сопоставления с образцом): экземпляр Monad для Either распространяет ошибки, как это делает монада Maybe (но с дополнительное значение 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 поймать эти исключения.)

11

Return r e типа, который вы описываете именно стандартный тип

data Either a b = Left a | Right b 

Возможно, вы захотите использовать так называемый "error monad" (более подходящее имя - «monad исключения») пакета mtl. (Кроме того, в пакете monadLib есть ExceptionT, если вы не хотите использовать mtl.) Это позволяет вам обрабатывать ошибки в чистом коде, вызывая throwError и catchError. Here вы можете найти пример, показывающий, как его использовать.