по тому на каких типах они определены. Так например функция sort :: Ord a => [a] -> [a] определена
не в Prelude, а в отдельном библиотечном модуле для списков он называется Data.List. Так же есть много
других модулей для разных типов, таких как Data.Bool, Data.Char, Data.Function, Data.Maybe и многие
другие. Не пугайтесь изобилия модулей постепенно они станут вашей опорой.
Для поиска в стандартных библиотеках есть замечательный интернет-сервис Hoogle (http://www.
haskell.org/hoogle/). Hoogle может искать значения не только по имени, но и по типам. Например мы
хотим узнать целочисленный код символа. Поиск по типу Char -> Int выдаёт искомую функцию digitToInt.
2.8 Краткое содержание
В этой главе мы познакомились с интерпретатором ghci и основными типами. Рассмотрели много при-
меров.
Документация | 37
Типы
Bool
– Основные операции: &&, ||, not, if c then t else e
Char
– Значения пишутся в ординарных кавычках, как в ’H’, ’+’
String
– Значения пишутся в двойных кавычках, как в ”Hello World”
Int
– Эффективные целые числа, но ограниченные
Integer
– Не ограниченные целые числа, но не эффективные
Double
– Числа с двойной точностью
Float
– Числа с ординарной точностью
Rational
– Дробные числа
Нам впервые встретились кортежи (на функции properFraction). Кортежи используются для возвраще-
ния из функции нескольких значений. Элементы кортежа могут иметь разные типы. Для извлечения элемен-
тов из кортежей-пар используются функции fst и snd. Кортежи пишутся в скобках, и элементы разделены
запятыми:
(a, b)
(a, b, c)
(a, b, c, d)
…
Классы
Show
Печать
Eq
Сравнение на равенство
Num
Сложение и умножение
Fractional
Деление
Особенности синтаксиса
Запись применения функции:
Префиксная
Инфиксная
add a b
a ‘add‘ b
(+) a b
a + b
Также мы научились приводить одни численные типы к другим и пользоваться документацией.
2.9 Упражнения
• Напишите функцию beside :: Nat -> Nat -> Bool, которая будет возвращать True только в том случае,
если два аргумента находятся рядом, то есть один из них можно получить через другой операцией Succ.
• Напишите функцию beside2 :: Nat -> Nat -> Bool, которая будет возвращать True только если
аргументы являются соседями через некоторое другое число.
• Мы написали очень неэффективную функцию сложения натуральных чисел. Проблема в том, что число
рекурсивных вызовов функции зависит от величины второго аргумента. Если мы захотим прибавить
единицу к сотне, то порядок следования аргументов существенно повлияет на скорость вычисления.
Напишите функцию, которая лишена этого недостатка.
• Напишите функцию возведения в степень pow :: Nat -> Nat -> Nat.
• Напишите тип, описывающий бинарные деревья BinTree a. Бинарное дерево может быть либо листом
со значением типа a, либо хранить два поддерева.
• Напишите функцию reverse :: BinTree a -> BinTree a, которая переворачивает дерево. Она меняет
местами два элемента в узле дерева.
• Напишите функцию depth :: BinTree a -> Nat, которая вычисляет глубину дерева, то есть самый
длинный путь от корня дерева к листу.
38 | Глава 2: Первая программа
• Напишите функцию leaves :: BinTree a -> [a], которая переводит бинарное дерево в список, воз-
вращая все элементы в листьях дерева.
• Обратите внимание на раздел List Operations в Prelude. Посмотрите на функции и их типы. Попро-
буйте догадаться по типу функции и названию что она делает.
• Попробуйте разобраться по документации с классами Ord (сравнение на больше/меньше), Enum (пере-
числения) и Integral (целые числа). Также стоит отметить класс Floating. Если у вас не получится,
не беда, они обязательно встретятся нам вновь. Там и разберёмся.
• Найдите функцию, которая переставляет элементы пары местами (элементы могут быть разных типов).
Потренируйтесь с кортежами. Определите аналоги функций fst и snd для не пар. Обратите внимание
на то, что сочетание символов (,) это функция-конструктор пары:
Prelude> (,) ”Hi” 101
(”Hi”,101)
Prelude> :t (,)
(,) :: a -> b -> (a, b)
Также определены („), („,) и другие.
Упражнения | 39
Глава 3
Типы
С помощью типов мы определяем все возможные значения в нашей программе. Мы определяем основные
примитивы и способы их комбинирования. Например в типе Nat:
data Nat = Zero | Succ Nat
Один конструктор-примитив Zero, и один конструктор Succ, с помощью которого мы можем делать со-
ставные значения. Определив тип Nat таким образом, мы говорим, что значения типа Nat могут быть только
такими:
Zero,
Succ Zero,
Succ (Succ Zero), Succ (Succ (Succ Zero)), …
Все значения являются цепочками Succ с Zero на конце. Если где-нибудь мы попытаемся построить значе-
ние, которое не соответствует нашему типу, мы получим ошибку компиляции, то есть программа не пройдёт
проверку типов. Так типы описывают множество допустимых значений.
Значения, которые проходят проверку типов мы будем называть допустимыми, а те, которые не проходят
соответственно недопустимыми. Так например следующие значения недопустимы для Nat
Succ Zero Zero,
Succ Succ, True, Zero (Zero Succ), …
Недопустимых значений конечно гораздо больше. Такое проявляется и в естественном языке, бессмыс-
ленных комбинаций слов гораздо больше, чем осмысленных предложений. Обратите внимание на то, что мы