дящие по типу функции. В этой маленькой функции кроется невероятная сила. Посмотрим на несколько
примеров:
Prelude Data.Char> :m -Data.Char
Prelude> let xs = [1,2,3,4,5]
Prelude> foldr (:) [] xs
[1,2,3,4,5]
Мы заменили конструкторы на самих себя и получили исходный список, теперь давайте сделаем что-
нибудь более конструктивное. Например вычислим сумму всех элементов или произведение:
Prelude> foldr (+) 0 xs
15
Prelude> foldr (*) 1 xs
120
Prelude> foldr max (head xs) xs
5
3.6 Краткое содержание
В этой главе мы присмотрелись к типам и узнали как ограничения, общие для всех типов, сказываются
на структуре значений. Мы узнали, что константы в Haskell очень похожи на деревья, а запись констант
– на строчную запись дерева. Также мы присмотрелись к функциям и узнали, что операция определения
синонима состоит из композиции и декомпозиции значений.
name
декомпозиция
=
композиция
Существует несколько правил для построения композиций:
• Одно для функций в префиксной форме записи:
f :: a -> b,
x :: a
-------------------------------
(f x) :: b
• И два для функций в инфиксной форме записи:
Это левое сечение:
(*) :: a -> (b -> c),
x :: a
---------------------------------
(x *) :: b -> c
И правое сечение:
(*) :: a -> (b -> c),
x :: b
---------------------------------
(* x) :: a -> c
Декомпозиция происходит в аргументах функции. С её помощью мы можем извлечь из составной
константы-дерева какую-нибудь часть или указать на какие константы мы реагируем в данном уравнении.
Ещё мы узнали о
аргумента, которые возвращают константы или другие функции одного аргумента.
Мы потренировались в составлении неправильных выражений и посмотрели как компилятор на основе
правил применения узнаёт что они неправильные. Мы узнали, что такое ограничение мономорфизма и как
оно появляется. Также мы присмотрелись к рекурсивным функциям.
56 | Глава 3: Типы
Succ
not
Рис. 3.7: Конструкторы и синонимы
3.7 Упражнения
• Составьте в интерпретаторе как можно больше неправильных выражений и посмотрите на сообще-
ния об ошибках. Разберитесь почему выражение оказалось неправильным. Для этого проверьте типы с
помощью правил применения. Составьте несколько выражений, ведущих к ошибке из-за ограничения
мономорфизма.
• Потренируйтесь в интерпретаторе с функциями map, filter и foldr. Попробуйте их с самыми разными
функциями. Воспользуйтесь и теми функциями, что были определены в прошлой главе в тексте или в
упражнениях.
• В этой главе было много картинок и графических аналогий, попробуйте попрограммировать в картин-
ках. Нарисуйте определённые нами функции или какие-нибудь новые в виде деревьев. Например, это
можно сделать так. Мы будем отличать конструкторы от синонимов. Конструкторы будем рисовать в
одинарном кружке, а синонимы в двойном.
one
=
Nat
Succ
Zero
Рис. 3.8: Синоним-константа
Мы будем все функции писать также как и прежде, но вместо аргументов слева от знака равно и выра-
жений справа от знака равно, будем рисовать деревья.
Например, объявим простой синоним-константу (рис. 3.8). Мы будем дорисовывать сверху типы зна-
чений вместо объявления типа функции.
Несколько функций для списков. Извлечение первого элемента (рис. 3.9) и функция преобразования
всех элементов списка (рис. 3.10). Попробуйте в таком же духе определить несколько функций.
Упражнения | 57
head
[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 существует несколько встроенных выражений, которые облегчают построение функций и дела-
ют код более наглядным. Их можно разделить на два вида: выражения, которые поддерживают
(expression style).
Что это за стили? В декларативном стиле определения функций больше похожи на математическую но-
тацию, словно это предложения языка. В композиционном стиле мы строим из маленьких выражений более
сложные, применяем к этим выражениям другие выражения и строим ещё большие.
В Haskell есть полноценная поддержка и того и другого стиля, поэтому конструкции которые мы рас-
смотрим в этой главе будут по смыслу дублировать друг друга. Выбор стиля скорее дело вкуса, существуют
приверженцы и того и другого стиля, поэтому разработчики Haskell не хотели никого ограничивать.
4.1 Локальные переменные
Вспомним формулу вычисления площади треугольника по трём сторонам:
√
Где
Как бы мы определили эту функцию теми средствами, что у нас есть? Наверное, мы бы написали так:
square a b c = sqrt (p a b c * (p a b c - a) * (p a b c - b) * (p a b c - c))