Является ли оператор && строгим в Haskell?

Например, у меня есть операция fnB :: a -> Bool, которая не имеет смысла, пока fnA :: Bool не вернет False. В C я могу составить эти две операции в одном блоке if:

if( fnA && fnB(a) ){ doSomething; }

и C гарантируют, что fnB не будет выполняться до тех пор, пока fnA не вернет false.

Но Haskell ленив, и, как правило, нет гарантии, что операция будет выполняться первой, пока мы не будем использовать seq, $! или что-то еще, чтобы сделать наш код строгим. Как правило, это то, что нам нужно быть счастливым. Но используя оператор &&, я ожидал бы, что fnB не будет оцениваться до тех пор, пока fnA не вернет результат. Предоставляет ли Haskell такую ​​гарантию &&? И будет ли Haskell оценивать fnB, даже когда fnA возвращает False?

Ответ 1

Функция (&&) является строгой во втором аргументе, только если ее первый аргумент True. Он всегда строг в своем первом аргументе. Эта строгость/лень - это то, что гарантирует порядок оценки.

Таким образом, он ведет себя точно так же, как и C. Разница в том, что в Haskell (&&) является обычной функцией. В C это было бы невозможно.

Но Haskell ленив, и, как правило, нет гарантии, что операция будет выполняться первой, пока мы не будем использовать seq, $!, или что-то еще, чтобы сделать наш код строгим.

Это неверно. Истина глубже.

Четкость курса:

Мы знаем, что (&&) является строгим в своем первом параметре, потому что:

⊥ && x = ⊥

Здесь ⊥ что-то вроде undefined или бесконечный цикл (⊥ произносится "снизу" ). Мы также знаем, что (False &&) не является строгим во втором аргументе:

False && ⊥ = False

Он не может оценить свой второй аргумент, потому что его второй аргумент ⊥ не может быть оценен. Однако функция (True &&) строго во втором аргументе, потому что:

True && ⊥ = ⊥

Итак, мы говорим, что (&&) всегда является строгим в своем первом аргументе и строгим во втором аргументе только тогда, когда первый аргумент True.

Порядок оценки:

Для (&&) его свойства строгости достаточно, чтобы гарантировать порядок выполнения. Это не всегда так. Например, (+) :: Int -> Int -> Int всегда является строгим в обоих аргументах, поэтому сначала можно оценить любой аргумент. Однако вы можете только сказать разницу, поймав исключения в монаде IO, или если вы используете функцию unsafe.

Ответ 2

Как отмечают другие, естественно, (&&) является строгим в одном из своих аргументов. По стандартным определениям оно строго по первому аргументу. Вы можете использовать flip для перевода семантики.

В качестве дополнительной заметки: обратите внимание, что аргументы (&&) не могут иметь побочных эффектов, поэтому есть две причины, по которым вам было бы интересно, строго ли x && y в y:

  • Производительность: если y занимает много времени, чтобы вычислить.
  • Семантика: если вы ожидаете, что y может быть внизу.

Ответ 3

Haskell ленив, и, как правило, нет никакой гарантии, какая операция будет выполняться первой

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

&& имеет 9-значную таблицу истинности, включая случаи, когда один или оба аргумента undefined, и он точно определяет операцию:

a           b           a && b
True        True        True
True        False       False
True        undefined   undefined
False       True        False
False       False       False
False       undefined   False
undefined   True        undefined
undefined   False       undefined
undefined   undefined   undefined

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

(Если вы изучите таблицу, вы заметите, что нет возможности для последовательной реализации следовать ей, если она не выполняет сначала a, а затем b iff a - True. Но реализация Haskell не требуется чтобы быть последовательным! Реализация всегда позволяет выполнять выполнение b всякий раз, когда захочет, ваша единственная гарантия заключается в том, что в соответствии с таблицей результат выполнения b может повлиять только на вашу программу, когда a - True.)

(Обратите внимание, что "лень" - единственный способ написать функцию с таблицей истинности, как и выше, на языке, таком как C или ML, все пять строк с undefined в любом аргументе будут силой чтобы иметь undefined в результате, где в Haskell (и в C, потому что && встроен в язык C), одна из строк может вместо этого иметь False.)

Ответ 4

Я считаю, что он работает так, как вы ожидаете; оценить RHS, если LHS оценивает значение True. Однако, полагая, что RHS не имеет побочных эффектов, как вы знаете (или заботитесь)?

Изменить: Я думаю, что RHS может быть undefined, и тогда вам будет интересно...