Подтвердить что ты не робот

Почему исключения Haskell могут быть захвачены только в монаде IO?

Может ли кто-нибудь объяснить, почему исключения могут быть выброшены за пределы монады IO, но могут быть пойманы внутри него?

4b9b3361

Ответ 1

Одной из причин является денотационная семантика Haskell.

Одной из опрятных свойств (чистых) функций Haskell является их монотонность - более определенный аргумент дает более определенное значение. Это свойство очень важно, например, рассуждать о рекурсивных функциях (прочитайте статью, чтобы понять, почему).

Обозначение исключения по определению - нижний, _|_, наименьший элемент в poset, соответствующий данному типу. Таким образом, для удовлетворения требования монотонности для любого обозначения f функции Хаскеля должно выполняться следующее неравенство:

f(_|_) <= f(X)

Теперь, если бы мы могли перехватывать исключения, мы могли бы разбить это неравенство на "распознавание" дна (перехват исключения) и возврат более определенного значения:

f x = case catch (seq x True) (\exception -> False) of
        True -> -- there was no exception
            undefined
        False -> -- there was an exception, return defined value
            42

Здесь полная рабочая демонстрация (требуется базовый 4 Control.Exception):

import Prelude hiding (catch)
import System.IO.Unsafe (unsafePerformIO)
import qualified Control.Exception as E

catch :: a -> (E.SomeException -> a) -> a
catch x h = unsafePerformIO $ E.catch (return $! x) (return . h)

f x = case catch (seq x True) (\exception -> False) of
        True -> -- there was no exception
            undefined
        False -> -- there was an exception, return defined value
            42

Другая причина, как отметил TomMD, заключается в нарушении ссылочной прозрачности. Вы можете заменить равные вещи равными и получить другой ответ. (Равно в денотационном смысле, т.е. Они обозначают одно и то же значение, а не в значении ==).

Как мы это сделаем? Рассмотрим следующее выражение:

let x = x in x

Это нерекурсивная рекурсия, поэтому она никогда не возвращает нам никакой информации и поэтому также обозначается символом _|_. Если бы мы могли поймать исключения, мы могли бы написать функцию f, такую ​​как

f undefined = 0
f (let x = x in x) = _|_

(Последнее всегда верно для строгих функций, потому что Haskell не предоставляет никаких средств для обнаружения не заканчивающихся вычислений - и не может в принципе, из-за Halting проблема.)

Ответ 2

Поскольку исключения могут нарушать ссылочную прозрачность.

Вероятно, вы говорите об исключениях, которые на самом деле являются прямыми результатами ввода. Например:

head [] = error "oh no!" -- this type of exception
head (x:xs) = x

Если вы жалуетесь, что не можете ловить ошибки, подобные этому, я утверждаю вам, что функции не должны полагаться на error или любые другие исключения, но вместо этого должны использовать правильный тип возврата (Maybe, Either, или, возможно, MonadError). Это заставляет вас иметь дело с исключительным условием более явным образом.

В отличие от вышеизложенного (и что вызывает проблему, стоящую за вашим вопросом), исключения могут быть вызваны такими сигналами, как условия отсутствия памяти, которые полностью не зависят от вычисляемого значения. Это явно не чистое понятие и должно жить в IO.

Ответ 3

Возможно, я ошибаюсь в своих объяснениях, но так понимаю.

Поскольку функции чисты в Haskell, компилятор имеет право оценивать их в любом порядке, который он пожелает, и он все равно дает тот же результат. Например, данная функция:

square :: Int -> Int
square x = x * x

выражение square (square 2) может быть оценено по-разному, но оно всегда сводится к тому же результату, который равен 16.

Если мы будем называть square откуда-то еще:

test x = if x == 2 
         then square x 
         else 0

square x можно оценить позже, "вне" функции test, когда это действительно необходимо. В этот момент стек вызовов может полностью отличаться от того, который вы ожидаете сказать в Java.

Итак, даже если мы хотели поймать потенциальное исключение, созданное square, где вы должны поместить часть catch?