Если я говорю let 5 = 10
, почему 5 + 1
возвращает 6
вместо 11
?
Что делает "пусть 5 = 10"? Разве это не операция присваивания?
Ответ 1
Когда вы говорите
let 5 = 10
это не переопределение 5, это сопоставление с образцом, то же самое происходит, когда вы говорите
foo 5 = undefined
... foo 10 ...
Образец просто терпит неудачу, если он когда-либо сопоставляется.
В let-выражениях матч ленив. Это означает, что совпадение выполняется только при оценке переменной, связанной с ней. Это позволяет нам писать такие вещи, как
let foo = undefined in 10
В вашем выражении никакая переменная не связана, поэтому шаблон никогда не сопоставляется.
Возможно, такие шаблоны без переменных не имеют смысла в let-bindings и должны быть обнаружены компилятором, но язык не запрещает их.
Ответ 2
В принципе,
let 5 = 10 in ...
эквивалентно
case 10 of ~5 -> ...
Обратите внимание на ~
, который обозначает ленивый или неопровержимый шаблон. Это шаблон, который соответствует всем, и откладывает соответствие до точки, в которой действительно требуется какая-либо переменная. В шаблоне 5
нет переменных, поэтому ничего не происходит.
Этот угловой случай совершенно бесполезен, и, возможно, компилятор должен выпустить предупреждение здесь.
Чтобы прояснить смысл ленивых шаблонов, рассмотрите это:
case f 3 of
(x,y) -> g 10 x y
здесь f 3
сначала оценивается (WHNF), выставляя конструктор пары. Тогда x,y
привязаны к (еще не оцененным) компонентам пары. Наконец, вычисляется g 10
, результат применяется к x
(который может потребоваться сейчас), а затем к y
(что может потребовать запроса x
или y
).
Для сравнения,
case f 3 of
~(x,y) -> g 10 x y
не начинается с оценки f 3
. Вместо этого x
привязан к неоцененному fst (f 3)
и y
привязан к неоцененному snd (f 3)
. Вместо этого мы начинаем с оценки g 10
. Затем мы применяем это к x
: это может вызвать запрос x
, вызывая оценку f 3
. Затем мы применяем результат к y
, вызывая аналогичную оценку. Большинство реализаций фактически разделяют результат f 3
между x
и y
, так что он вычисляется не более одного раза.
Ответ 3
Как @n.m. говорит, что вы образец соответствия. Вот несколько примеров.
Соответствие шаблонов может быть успешным
Prelude> let (a, 10) = (15, 10) in a
15
или сбой.
Prelude> let (a, 10) = (15, 15) in a
*** Exception: <interactive>:5:5-22: Irrefutable pattern failed for pattern (a, 10)
Так как Haskell ленив, ваш код будет успешным, если вы не используете результирующее значение. Это, по сути, то, что вы делаете:
Prelude> let (a, 10) = (15, 15) in "Something else"
"Something else"
Обратите внимание, что типы все еще должны проверять:
Prelude> let (a, 10, 999) = (15, 15) in "Something else"
<interactive>:7:20: error:
• Couldn't match expected type ‘(t, Integer, Integer)’
with actual type ‘(Integer, Integer)’
• In the expression: (15, 15)
In a pattern binding: (a, 10, 999) = (15, 15)
In the expression: let (a, 10, 999) = (15, 15) in "Something else"
• Relevant bindings include a :: t (bound at <interactive>:7:6)