Haskellers действительно стремятся избегать выброса исключений из функций - функция должна генерировать исключение только в действительно исключительных случаях, то есть в ситуациях «этого никогда не должно происходить», например, когда пользователь передает какой-то ввод, который явно запрещенпо документации.Если бы чистые функции регулярно вызывали исключения, это было бы большой проблемой не только потому, что тип не говорит, что нужно подготовить для перехвата, а также один не может перехватывать исключения в чистой функции, но только вIO
код, который вызывает функцию.И даже если вы в принципе можете поймать исключение, может быть трудно предсказать , где это нужно сделать, потому что ленивая оценка может отсрочить момент, когда это действительно происходит.
В сущностина самом деле, даже в случае, например, Wreq.get
, исключение не выдается из функции .
Prelude Network.Wreq> get "htt:/p/this-isn't even valid URL syntax" `seq` "Ok"
"Ok"
Это IO
действие , которое выдает при его выполнении:
Prelude Network.Wreq> get "htt:/p/this-isn't even valid URL syntax" >> pure ()
*** Exception: InvalidUrlException "htt:/p/this-isn't%20even%20valid%20URL%20syntax" "Invalid scheme"
Теперь с действием IO
ситуация несколько иная. Lots из IO
действий могут иметь потенциально очень разные ошибки в разных ситуациях, которые могут быть трудно или невозможно предсказать, например, сбой жесткого диска.Каталогизация всех возможных ошибок в подходящем типе данных для каждого действия была бы серьезной задачей, и было бы действительно довольно обременительно обрабатывать каждый возможный случай или выяснять, какие части просто передать.А простое завершение результата каждого отдельного действия IO
в Maybe
просто приведет к ситуации, аналогичной Java, где каждая ссылка может быть нулевой.Это ничего вам не говорит, и люди часто не могут придумать разумных способов справиться с этим.
Это в значительной степени проблема, почему исключения были изобретены в первую очередь, и это так же, какхорошо для процедурных языков, как это имеет место для Haskell (или, скорее, это процедурный eDSL, который IO
).И поскольку в отличие от чистых функций, IO
имеет четко определенную временную последовательность в своем потоке управления, также довольно ясно, где вы должны это сделать, если вам нужно поймать какое-то конкретное исключение.
Это нескажем, для действия IO
никогда не имеет смысла возвращать значение Maybe
/ Either
, которое ясно указывает на возможные ошибки, просто это не всегда стоит.