Как работают исключения в Haskell?


86

В GHCi:

Prelude> error (error "")
*** Exception: 
Prelude> (error . error) ""
*** Exception: *** Exception: 

Почему первое не является вложенным исключением?


9
Это преобразование, которое GHC может делать: «Я - компилятор, который работает сам по себе, и все _ | _s мне одинаковы». Вы спрашиваете детали реализации, благодаря которым эти две строки компилируются по-разному?
shachaf

3
errorособенный и не совсем исключительный механизм. Для реальных, улавливаемых исключений см Error. Monad .
Cat Plus Plus

1
Обратите внимание, например, что эта функция (\f g x -> f (g x)) error error ""отличается от (.) error error "", даже если эта функция эквивалентна (.). Возможно, это связано с флагами оптимизации, с которыми был скомпилирован Prelude.
shachaf

5
Также iterate error "" !! nи офигенно fix error.
Vitus

9
Я всегда это делаю error = errorи программирую соответственно.
Габриэль Гонсалес

Ответы:


101

Ответ заключается в том, что это (несколько удивительная) семантика неточных исключений.

Когда можно показать, что чистый код оценивает набор исключительных значений (т. Е. Значение errorили undefined, а явно не вид исключений, сгенерированных в IO ), тогда язык разрешает возвращать любое значение этого набора. Исключительные значения в Haskell больше похожи NaNна код с плавающей запятой, чем на исключения на основе потока управления в императивных языках.

Иногда даже для продвинутых хаскеллеров возникает такая проблема, как:

 case x of
   1 -> error "One"
   _ -> error "Not one"

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

Почему мы это делаем? Потому что в противном случае мы чрезмерно ограничили бы порядок оценки языка, например, нам пришлось бы исправить детерминированный результат для:

 f (error "a") (error "b")

например, требуя, чтобы он оценивался слева направо, если присутствуют значения ошибок. Очень не-Аскелли!

Поскольку мы не хотим нарушать оптимизацию, которую можно выполнить в нашем коде только для поддержки error, решение состоит в том, чтобы указать, что результатом является недетерминированный выбор из набора исключительных значений: неточные исключения! В некотором смысле возвращаются все исключения, и выбирается одно.

Обычно вам все равно - исключение является исключением - если вы не заботитесь о строке внутри исключения, и в этом случае использование errorдля отладки сильно сбивает с толку.


Ссылки: Семантика для неточных исключений , Саймон Пейтон Джонс, Аластер Рид, Тони Хоар, Саймон Марлоу, Фергус Хендерсон. Разработка и реализация языков программирования Proc (PLDI'99), Атланта. ( PDF )


2
Я понимаю, что GHC выбирает одно из любых возможных исключений. Но в вашем примере «case» исключение «Not one» не может быть обнаружено для ввода 1, поэтому я бы все равно классифицировал это как ошибку.
Peaker

8
@Peaker dead code elim - оптимизатору не нужно смотреть на x, чтобы увидеть, что результатом является ошибка, все ветви выдают "одинаковое" значение, поэтому он может полностью игнорировать входное значение. Это не ошибка, за неточными исключениями!
Дон Стюарт

1
@lpsmith: я думаю, что все типы исключений неточны (когда выбрасываются с помощью throw) и что вы можете детерминированно генерировать исключение с помощью throwIO.
FunctorSalad

4
@ Пикер, я думаю, ты прав. Я не думаю, что ghc следует оптимизировать это выражение, если они хотят играть по правилам, изложенным в документе о неточных исключениях.
август

3
В документе о неточных исключениях , по- видимому, говорится, что если проверяемый случай является значением ошибки, то значения ошибок из ветвей могут быть возвращены. Так что case error "banana" of (x:xs) -> error "bonobo"могу дать вам * Exception: bonobo.
Бен Миллвуд
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.