haskell-notes

Упражнения | 85

Глава 6

Функторы и монады: теория

Мы научились комбинировать функции наиболее общего типа a -> b. В этой главе мы посмотрим на

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

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

вычислить значение или упасть, или функции, которые возвращают сразу несколько вариантов значений.

Для составления таких функций из простейших в Haskell предусмотрено несколько классов типов. Это функ-

торы и монады. Их мы и рассмотрим в этой главе.

6.1 Композиция функций

Центральной функцией этой главы будет функция композиции. Вспомним её определение для функций

общего типа:

(. ) :: (b -> c) -> (a -> b) -> (a -> c)

f . g = x -> f (g x)

Композиция двух функций f и g это такая функция, в которой мы сначала применяем g, а затем f. Для того

чтобы тип функции стал более наглядным, мы определим эту функцию немного по-другому. Мы поменяем

аргументы местами.

(>> ) :: (a -> b) -> (b -> c) -> (a -> c)

f >> g = x -> g (f x)

Мы будем изображать функции кружками, а значения – стрелками (рис. 6.1). Значения словно текут от

узла к узлу по стрелкам. Поскольку тип стрелки выходящей из f совпадает с типом стрелки входящей в g мы

можем соединить их и получить составную функцию (f>> g).

a

f

b

b

g

c

b

a

g

f

c

a

f>>g

c

Рис. 6.1: Композиция функций

86 | Глава 6: Функторы и монады: теория

Класс Category

С помощью операции композиции можно обобщить понятие функции. Для этого существует класс

Category:

class Category cat where

id

:: cat a a

(>> ) :: cat a b -> cat b c -> cat a c

Функция cat это тип с двумя параметрами, в котором выделено специальное значение id, которое остав-

ляет аргумент без изменений. Также мы можем составлять из простых функций сложные с помощью компо-

зиции, если функции совпадают по типу. Здесь мы для наглядности также заменили метод (. ) на (>> ), но

суть остаётся прежней. Для любого экземпляра класса должны выполняться свойства:

f

>> id

== f

id >> f

== f

f >> (g >> h) == (f >> g) >> h

Первые два свойства говорят о том, что id является нейтральным элементом для (>> ) слева и справа.

Третье свойство говорит о том, что нам не важно в каком порядке проводить композицию. Можно проверить,

что эти правила выполнены для функций.

Специальные функции

Все специальные функции, которые мы рассмотрим в этой главе будут иметь один и тот же тип:

a -> m b

Смотрите вместо произвольного типа b функция возвращает m b. Единственное, что будет меняться от

раздела к разделу это тип m. Добавив этот тип к результату, мы сузили область значений функции. Простым

примером таких функций могут быть функции, которые возвращают списки:

a -> [b]

Если раньше наши функции могли возвращать произвольное значение b, то теперь мы знаем, что все

результирующие значения таких функций будут списками.

При этом для каждого такого m мы попытаемся построить свой замкнутый мир специальных функций a

-> m b. Он будет жить внутри вселенной всех произвольных функций типа a -> b. В этом нам поможет

специальный класс типов, который называется категорией Клейсли (эта конструкция носит имя математика

Хенрика Клейсли).

class Kleisli m where

idK

:: a -> m a

(*> ) :: (a -> m b) -> (b -> m c) -> (a -> m c)

Этот класс является классом Category в мире наших специальных функций. Если мы сотрём все буквы m,

то мы получим обычные типы для тождества и композиции. В этом мире должны выполняться те же правила:

f

*> idK

== f

idK *> f

== f

f *> (g *> h) == (f *> g) *> h

Взаимодействие с внешним миром

С помощью класса Kleisli мы можем составлять из одних специальных функций другие. Но как мы

сможем комбинировать специальные функции с обычными?

Поскольку слева у нашей специальной функции обычный общий тип, то с этой стороны мы можем вос-

пользоваться обычной функцией композиции >> . Но как быть при композиции справа? Нам нужна функция

типа:

(a -> m b) -> (b -> c) -> (a -> m c)

Оказывается мы можем составить её из методов класса Kleisli. Мы назовём эту функцию композиции

(+> ).

(+> ) :: Kleisli m => (a -> m b) -> (b -> c) -> (a -> m c)

f +> g = f *> (g >> idK)

С помощью метода idK мы можем погрузить в мир специальных функций любую обычную функцию.

Композиция функций | 87

Три композиции

У нас появилось много композиций целых три:

аргументы

|

результат

обычная

>>

обычная

==

обычная

специальная

+>

обычная

==

специальная

специальная

*>

специальная

==

специальная

При этом важно понимать, что по смыслу это три одинаковые функции. Они обозначают операцию по-

следовательного применения функций. Разные значки отражают разные типы функций аргументов.

Обобщённая формулировка категории Клейсли

Отметим, что мы могли бы сформулировать класс Kleisli и в более общем виде с помощью класса

Category:

class Kleisli m where

idK

:: Category cat => cat a (m a)

(*> ) :: Category cat => cat a (m b) -> cat b (m c) -> cat a (m c)

(+> ) :: (Category cat, Kleisli m)

Страницы: 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