Вот для чего нужны монады! (Ну, не только это, но обработка исключений - одно из применений монадических идиом)
Вы делаете измените сигнатуру функций, которые могут выйти из строя (потому что они меняют семантику, и вы хотите отразить как можно большую часть семантики в типе, как правило). Но код, который использует эти функции, не обязательно должен соответствовать шаблону каждой отказавшей функции; они могут связываться, если им все равно:
head :: [a] -> Maybe a
eqHead :: (Eq a) => [a] -> Maybe [a]
eqHead xs = do
h <- head xs
return $ filter (== h) xs
Таким образом, eqHead
не может быть написано "чисто" (синтаксический выбор, альтернативы которого я хотел бы изучить), но он также не должен знать о Maybe
-ности head
, это нужно только знать, что head
может потерпеть неудачу некоторым способом.
Это не идеально, но идея в том, что функции в Haskell не имеют такой же стиль, как Java. В типичном дизайне Haskell исключение обычно не происходит глубоко в цепочке вызовов. Вместо этого, глубокая цепочка вызовов полностью чиста, когда мы знаем, что все аргументы полностью определены и хорошо себя ведут, и проверка происходит на самых внешних уровнях. Таким образом, исключение, поднимающееся из глубины, на самом деле не является чем-то, что должно поддерживаться на практике.
Вопрос о том, является ли сложность выполнения причиной шаблонов проектирования или шаблонов проектирования причиной отсутствия функций, поддерживающих это, является предметом обсуждения.