Является ли оператор && строгим в Хаскеле?
Например, у меня есть операция fnB :: a -> Bool
это не имеет смысла, пока fnA :: Bool
возвращается False
, В КИ могут составить эти две операции в одну if
блок:
if( fnA && fnB(a) ){ doSomething; }
и C будет гарантировать, что fnB
не будет исполняться до fnA
возвращает ложь
Но Haskell ленив, и, как правило, нет никакой гарантии, какая операция будет выполняться первой, пока мы не используем seq
, $!
или что-то еще, чтобы сделать наш код строгим. Вообще, это то, что нам нужно для счастья. Но используя &&
оператор, я ожидал бы, что fnB
не будет оцениваться до fnA
возвращает свой результат. Предоставляет ли Haskell такую гарантию &&
? И оценит ли Хаскелл fnB
даже когда fnA
возвращает Ложь?
4 ответа
Функция (&&)
строг в своем втором аргументе, только если его первый аргумент True
, Он всегда строг в своем первом аргументе. Эта строгость / лень это то, что гарантирует порядок оценки.
Так что он ведет себя точно так же, как C. Разница в том, что в Haskell, (&&)
это обычная функция. В Си это было бы невозможно.
Но Haskell ленив, и, как правило, нет никакой гарантии, какая операция будет выполняться первой, пока мы не используем seq, $!, или что-то еще, чтобы сделать наш код строгим.
Это не правильно. Истина глубже.
Ускоренный курс в строгости:
Мы знаем (&&)
строг в своем первом параметре, потому что:
⊥ && x = ⊥
Здесь something это что-то вроде undefined
или бесконечный цикл (⊥ произносится как "дно"). Мы также знаем, что (False &&)
не является строгим во втором аргументе:
False && ⊥ = False
Он не может оценить свой второй аргумент, потому что его второй аргумент - ⊥, который не может быть оценен. Тем не менее, функция (True &&)
строг в своем втором аргументе, потому что:
True && ⊥ = ⊥
Итак, мы говорим, что (&&)
всегда строг в своем первом аргументе и строг в своем втором аргументе, только когда первый аргумент True
,
Порядок оценки:
За (&&)
его строгости достаточно, чтобы гарантировать порядок исполнения. Это не всегда так. Например, (+) :: Int -> Int -> Int
всегда строг в обоих аргументах, поэтому любой аргумент может быть оценен первым. Однако вы можете заметить разницу, только обнаружив исключения в IO
монада, или если вы используете unsafe
функция.
Как отметили другие, естественно (&&)
строг в одном из своих аргументов. По стандартному определению он строг в своем первом аргументе. Ты можешь использовать flip
перевернуть семантику.
В качестве дополнительного примечания: обратите внимание, что аргументы (&&)
не может иметь побочных эффектов, поэтому есть только две причины, по которым вы бы хотели x && y
строго в y
:
- Производительность: если
y
занимает много времени, чтобы вычислить. - Семантика: если вы ожидаете, что
y
может быть снизу.
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
тогда и только тогда a
правда. Но реализации Haskell не обязательно должны быть последовательными! Реализация всегда может быть запущена b
когда захочет; Ваша единственная гарантия заключается в том, что, согласно таблице, результат выполнения b
может повлиять на вашу программу только тогда, когда a
правда.)
(Обратите внимание, что "лень" - это единственный способ написать функцию с таблицей истинности, подобной приведенной выше; на языке, подобном C или ML, все пять строк с undefined
в любом аргументе будет иметь силу иметь undefined
в результате, где в Haskell (и в C, потому что &&
встроен в язык Си) одна из строк может иметь False
как результат вместо этого.)
Я считаю, что это работает так, как вы ожидаете; оцените RHS, если LHS оценивает в True. Однако, если у RHS нет побочных эффектов, как бы вы узнали (или позаботились)?
Редактировать: я думаю, что RHS может быть undefined
и тогда тебе будет все равно...