[a]
=
a
:
x
x
Рис. 3.9: Функция извлечения первого элемента списка
map
a->b
[a]
=
[b]
[]
[]
f
map
a->b
[a]
=
[b]
:
:
f
x
xs
map
f
x
f
xs
Рис. 3.10: Функция преобразования элементов списка
58 | Глава 3: Типы
Глава 4
Декларативный и композиционный
стиль
В Haskell существует несколько встроенных выражений, которые облегчают построение функций и дела-
ют код более наглядным. Их можно разделить на два вида: выражения, которые поддерживают декларативный
стиль (declarative style) определения функций, и выражения которые поддерживают композиционный стиль
(expression style).
Что это за стили? В декларативном стиле определения функций больше похожи на математическую но-
тацию, словно это предложения языка. В композиционном стиле мы строим из маленьких выражений более
сложные, применяем к этим выражениям другие выражения и строим ещё большие.
В Haskell есть полноценная поддержка и того и другого стиля, поэтому конструкции которые мы рас-
смотрим в этой главе будут по смыслу дублировать друг друга. Выбор стиля скорее дело вкуса, существуют
приверженцы и того и другого стиля, поэтому разработчики Haskell не хотели никого ограничивать.
4.1 Локальные переменные
Вспомним формулу вычисления площади треугольника по трём сторонам:
√
S =
p · ( p − a ) · ( p − b ) · ( p − c )
Где a , b и c – длины сторон треугольника, а p это полупериметр.
Как бы мы определили эту функцию теми средствами, что у нас есть? Наверное, мы бы написали так:
square a b c =sqrt (p a b c *(p a b c -a) *(p a b c -b) *(p a b c -c))
p a b c =(a +b +c) /2
Согласитесь это не многим лучше чем решение в лоб:
square a b c =sqrt ((a +b +c) /2 *((a +b +c) /2 -a) *((a +b +c) /2 -b) *((a +b +c) /2 -c)) И в том и в другом случае нам приходится дублировать выражения, нам бы хотелось чтобы определение
выглядело так же, как и обычное математическое определение:
square a b c =sqrt (p *(p -a) *(p -b) *(p -c))
p =(a +b +c) /2
Нам нужно, чтобы p знало, что a, b и c берутся из аргументов функции square. В этом нам помогут
локальные переменные.
where-выражения
В декларативном стиле для этого предусмотрены where-выражения. Они пишутся так:
square a b c =sqrt (p *(p -a) *(p -b) *(p -c))
wherep =(a +b +c) /2
| 59
Или так:
square a b c =sqrt (p *(p -a) *(p -b) *(p -c)) where
p =(a +b +c) /2
За определением функции следует специальное слово where, которое вводит локальные имена-
синонимы. При этом аргументы функции включены в область видимости имён. Синонимов может быть
несколько:
square a b c =sqrt (p *pa *pb *pc)
wherep
=(a +b +c) /2
pa =p -a
pb =p -b
pc =p -c
Отметим, что отступы обязательны. Haskell по отступам понимает, что эти выражения относятся к where.
Как и в случае объявления функций порядок следования локальных переменных в where-выражении не
важен. Главное чтобы в выражениях справа от знака равно мы пользовались именами из списка аргументов
исходной функции или другими определёнными именами. Локальные переменные видны только в пределах
той функции, в которой они вводятся.
Что интересно, слева от знака равно в where-выражениях можно проводить декомпозицию значений, так-
же как и в аргументах функции:
pred :: Nat -> Nat
pred x =y
where( Succy) =x
Эта функция делает тоже самое что и функция
pred :: Nat -> Nat
pred ( Succy) =y
В where-выражениях можно определять новые функции а также выписывать их типы:
add2 x =succ (succ x)
wheresucc :: Int -> Int
succ x =x +1
А можно и не выписывать, компилятор догадается:
add2 x =succ (succ x)
Читать дальше