[haskell] обработка ошибок с помощью исключений

Landstreicher

Предположим я вычисляю некоторое выражение, в процессе его вычисления могут возникать некоторые ошибки (деление на 0, неправильная функция, нет места на диске итп).
Почти во всех современных языках (в том числе C++,C#,Java,ML,Lisp,Python) я могу написать примерно следующее:

try {
x = f(a) + g(b) + h(c);
} catch (exception& e) {
printf("oops! error occured: %s\n", e.what;
}

В Lisp даже есть возможность спросить что-то у пользователя, поменять что-то, а затем назад вернуться в то место, где была ошибка и продолжить работу (handler-bind, restart итп).
Насколько я понимаю, эквивалентная функциональность в Haskell-е реализуется следующим образом:

data ErrVal a = OK a | Error Exception
case f a of
Error ex -> Error ex
OK v1 -> case g b of
Error ex2 -> Error ex2
Ok v2 -> case h c of
Error ex3 -> Error ex3
Ok v3 -> v1 + v2 + v3

Такой способ, возвращает нас в старые добрые 70-ые годы, когда никаких еще исключений в помине не было, функции возвращали код возврата, который надо было проверять руками по типа if (result == -1) { ... }.
Вопрос: есть ли какой-нибудь способ реализовать нормальную обработку исключений в Haskell-е, так чтобы не пришлось писать кучу однообразного кода?

Papazyan

Если видишь ступенеобразную конструкцию, значит надо использовать монаду. В данном случае есть даже некий выбор монад. Maybe, Either, Control.Exception, Control.Monad.Error.

Landstreicher

> Если видишь ступенеобразную конструкцию, значит надо использовать монаду. В данном случае есть даже некий выбор монад. Maybe, Either, Control.Exception, Control.Monad.Error.
Использование монады в данном случае не дает никакого улучшения и не решает проблему написания однообразного кода в больших количествах. Если ты не согласен --- приведи пример кода.

Papazyan


data ErrVal a = OK a | Error Exception
return x = OK x
fail x = Error x
m =>> f =
case m of
OK x -> f x
Error x -> Error x
do
v1 <- f a
v2 <- g b
v3 <- h c
return $ v1 + v2 + v3

Landstreicher

Данное решение применимо только в пределах одной функции.
Рассмотрим такую ситуацию: f1 вызывает f2, f2 вызывает f3, ..., f19 вызывает в f20.
Предположим, в f20 кидается исключение, в f1 оно ловится.
В подходе, используемом в Java, C# --- f2, f3, ..., f19 не знают про исключение.
В Haskell функции f2, ..., f19 должны знать про исключение, что сильно увеличивает размер программ (не содержательно, а требуя везде расстановки всяких do, IO не относящихся к смыслу программы.
Что самое важное, сильно увеличивается связность. Если например, f20 раньше не кидало исключения, а теперь стало кидать, или же стало кидать новые типы исключений, то придется перепеписывать f2-f19. Все функции становятся зависимыми, что ставит крест на модульном программировании.
IMHO возможность разделения программы на куски, которые можно писать независимо, более важна чем любые языкове фичи типа монад, type inference итп.

Papazyan

Haskell ленивый, что же ты от него хочешь.
У меня есть 2 гипотезы
1) это такой геморой, который невозможно обойти.
2) мы просто не знаем, что такие вещи можно делать как-то иначе. Надо посмотреть исходники GHC.

Landstreicher

> Haskell ленивый, что же ты от него хочешь.
Ааа! То есть, в ленивом языке g может быть вызван раньше, чем f и соответственно непонятно, какой exception кидать? Вот в чем фишка!
Не работал с ленивыми языками, поэтому сразу не дошло.
Получается проблема глубже, чем я изначально предполагал.

Landstreicher

Вот что удалось найти в Google: http://research.microsoft.com/~simonpj/Papers/imprecise-exn....
IMHO толково написано, проливает свет на подобные вопросы.
Оставить комментарий
Имя или ник:
Комментарий: