haskell-notes

график. Например, есть функции:

jonny :: Week -> Status

jonny x = …

colin :: Week -> Status

colin x = …

Конкретное определение сейчас не важно, важно, что они определяют зависимость статуса (Status) от

дня недели (Week) для работников Джонни (jonny) и Колина (colin).

Также у нас есть полезная функция:

calendar :: Date -> Week

calendar x = …

Она определяет по дате день недели. И теперь, зная лишь эти функции, мы можем спросить у вычислителя

будет ли у Джонни выходной 8 августа 3043 года:

jonny (calendar (Date (Year 3043) August (Day 8)))

=> jonny Saturday

=> Rest

Интересно, у нас опять всего лишь два значения, но, дав такое большое имя одному из значений, мы

смогли получить полезную нам информацию, ничего не вычисляя.

1.4 Классы типов

Если типы и значения – привычные понятия, которые можно найти в том или ином виде в любом языке

программирования, то термин класс типов встречается не часто. У него нет аналогов и в обычном языке,

поэтому я сначала постараюсь объяснить его смысл на примере.

В типизированном языке у каждой функции есть тип, но бывают функции, которые могут быть опреде-

лены на аргументах разных типов; по сути, они описывают схожие понятия, но определены для значений

разных типов. Например, функция сравнения на равенство, говорящая о том, что два значения одного типа

a равны, имеет тип a -> a -> Bool, или функция печати выражения имеет тип a -> String, но что такое

a в этих типах? Тип a является любым типом, для которого сравнение на равенство или печать (преобразо-

вание в строку) имеют смысл. Это понятие как раз и кодируется в классах типов. Классы типов (type class)

позволяют определять функции с одинаковым именем для разных типов.

У классов типов есть имена. Также как и имена классов, они начинаются с большой буквы. Например,

класс сравнений на равенство называется Eq (от англ. equals – равняется), а класс печати выражений имеет

имя Show (от англ. show – показывать). Посмотрим на их определения:

Класс Eq:

class Eq a where

(==) :: a -> a -> Bool

(/=) :: a -> a -> Bool

Класс Show:

1Было бы точнее называть вычислитель редуктором, поскольку мы проводим редукции, или замену эквивалентных значений, но

закрепилось это название. К тому же, редуктор также обозначает прибор.

18 | Глава 1: Основы

class Show a where

show :: a -> String

За ключевым словом class следует имя класса, тип-параметр и ещё одно ключевое слово where. Далее с

отступами пишутся имена определённых в классе значений. Значения класса называются методами.

Мы определяем лишь типы методов, конкретная реализация будет зависеть от типа a. Методы определя-

ются в экземплярах классов типов, мы скоро к ним перейдём.

Программистская аналогия класса типов это интерфейс. В интерфейсе определён набор значений (как

констант, так и функций), которые могут быть применены ко всем типам, которые поддерживают данный

интерфейс. К примеру, в интерфейсе “сравнение на равенство” для некоторого типа a определены две функ-

ции: равно (==) и не равно (/=) с одинаковым типом a -> a -> Bool, или в интерфейсе “печати” для любого

типа a определена одна функция show типа a -> String.

Математическая аналогия класса типов это алгебраическая система. Алгебра изучает свойства объекта в

терминах операций, определённых на нём, и взаимных ограничениях этих операций. Алгебраическая систе-

ма представляет собой набор операций и свойств этих операций. Этот подход позволяет абстрагироваться

от конкретного представления объектов. Например группа – это все объекты данного типа a, для которых

определены значения: константа – единица типа a, бинарная операция типа a -> a -> a и операция взятия

обратного элемента, типа a -> a. При этом на операции накладываются ограничения, называемые свойства-

ми операций. Например, ассоциативность бинарной операции, или тот факт, что единица с любым другим

элементом, применённые к бинарной операции, дают на выходе исходный элемент.

Давайте определим класс для группы:

class Group a where

e

:: a

(+) :: a -> a -> a

inv :: a -> a

Класс с именем Group имеет для некоторого типа a три метода: константу e :: a, операцию (+) :: a ->

a -> a и операцию взятия обратного элемента inv :: a -> a.

Как и в алгебре, в Haskell классы типов позволяют описывать сущности в терминах определённых на них

операций или значений. В примерах мы указываем лишь наличие операций и их типы, так же и в классах

типов. Класс типов содержит набор имён его значений с информацией о типах значений.

Определив класс Group, мы можем начать строить различные выражения, которые будут потом интер-

претироваться специфическим для типа образом:

twice :: Group a => a -> a

twice a = a + a

isE :: (Group a, Eq a) => a -> Bool

isE x = (x == e)

Обратите внимание на запись Group a => и (Group a, Eq a) => . Это называется контекстом объявления

типа. В контексте мы говорим, что данный тип должен быть из класса Group или из классов Group и Eq. Это

значит, что для этого типа мы можем пользоваться методами из этих классов.

В первой функции twice мы воспользовались методом (+) из класса Group, поэтому функция имеет кон-

Страницы: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162