>ghci> (let a = 10; b = 20 in a*b, let foo="Эй, "; bar = "там!" in foo ++ bar)
>(200,"Эй, там!")
Как мы уже говорили ранее, определения в секции >let
могут использоваться при сопоставлении с образцом. Они очень полезны, к примеру, для того, чтобы быстро разобрать кортеж на элементы и привязать значения элементов к переменным, а также в других подобных случаях.
>ghci> (let (a,b,c) = (1,2,3) in a+b+c) * 100
>600
Если определения >let
настолько хороши, то почему бы только их всё время и не использовать? Ну, так как это всего лишь выражения, причём с локальной областью видимости, то их нельзя использовать в разных охранных выражениях. К тому же некоторые предпочитают, чтобы их переменные вычислялись после использования в теле функции, а не до того. Это позволяет сблизить тело функции с её именем и типом, что способствует большей читабельности.
Выражения let в генераторах списков
Давайте перепишем наш предыдущий пример, который обрабатывал списки пар вида (вес, рост), чтобы он использовал секцию >let
в выражении вместо того, чтобы определять вспомогательную функцию в секции >where
.
>calcBmis :: [(Double, Double)] -> [Double]
>calcBmis xs = [bmi | (w, h) <– xs, let bmi = w / h 2]
Мы поместили выражение >let
в генератор списка так, словно это предикат, но он не фильтрует список, а просто определяет имя. Имена, определённые в секции >let
внутри генератора списка, видны в функции вывода (часть до символа |) и для всех предикатов и секций, которые следуют после ключевого слова >let
. Так что мы можем написать функцию, которая выводит только толстяков:
>calcBmis :: [(Double, Double)] -> [Double]
>calcBmis xs = [bmi | (w, h) <– xs, let bmi = w / h ^ 2, bmi > 25.0]
Использовать имя >bmi
в части >(w, h) <– xs
нельзя, потому что она расположена до ключевого слова >let
.
Выражения let в GHCi
Часть >in
также может быть пропущена при определении функций и констант напрямую в GHCi. В этом случае имена будут видимы во время одного сеанса работы GHCi.
>ghci> let zoot x y z = x * y + z
>ghci> zoot 3 9 2
>29
>ghci> let boot x y z = x * y + z in boot 3 4 2
>14
>ghci> boot
>
Поскольку в первой строке мы опустили часть >in
, GHCi знает, что в этой строке >zoot
не используется, поэтому запомнит его до конца сеанса. Однако во втором выражении >let
часть >in
присутствует, и определённая в нём функция >boot
тут же вызывается. Выражение >let
, в котором сохранена часть >in
, является выражением и представляет некоторое значение, так что GHCi именно это значение и печатает.
Выражения для выбора из вариантов
Во многих императивных языках (C, C++, Java, и т. д.) имеется оператор >case
, и если вам доводилось программировать на них, вы знаете, что это такое. Вы берёте переменную и выполняете некую часть кода для каждого значения этой переменной – ну и, возможно, используете финальное условие, которое срабатывает, если не отработали другие.
Язык Haskell позаимствовал эту концепцию и усовершенствовал её. Само имя «выражения для выбора» указывает на то, что они являются… э-э-э… выражениями, так же как >if
– >then