= Just a
В этой функции мы перекладываем элементы из списка [a] в частично определённое значение Maybe.
Тоже самое происходит и в функции concat:
230 | Глава 15: Теория категорий
concat :: [[a]] -> [a]
Элементы перекладываются из списка списков в один список. В теории категорий этот процесс называ-
ется естественным преобразованием. Структуры определяются функторами. Поэтому в определении будет
участвовать два функтора. В функции onlyOne это были функторы [] и Maybe. При перекладывании элемен-
тов мы можем просто выбросить все элементы:
burnThemALl :: [a] -> ()
burnThemAll = const ()
Можно сказать, что единичный тип также определяет функтор. Это константный функтор, он переводит
любой тип в единственное значение (), а функцию в id:
data Empty a = Empty
instance Functor Empty where
fmap = const id
Тогда тип функции burnThemAll будет параметризован и слева и справа от стрелки:
burnThemAll :: [a] -> Empty a
burnThemAll = const Empty
Пусть даны две категории
для любого
Рассмотрим преобразование onlyOne :: [a] -> Maybe a. Категории
это категория Hask. Функтор
объекта a из Hask определяет стрелку
onlyOne :: [a] -> Maybe a
Так мы получаем семейство стрелок, параметризованное объектом из Hask:
onlyOne :: [Int] -> Maybe Int
onlyOne :: [Char] -> Maybe Char
onlyOne :: [Int -> Int] -> Maybe (Int -> Int)
...
...
Теперь давайте определим, что значит перекладывать из одной структуры в другую, не меняя содержа-
ния. Представим, что функтор – это контейнер. Мы можем менять его содержание с помощью метода fmap.
Например мы можем прибавить единицу ко всем элементам списка xs с помощью выражения fmap (+1) xs.
Точно так же мы можем прибавить единицу к частично определённому значению. С точки зрения теории ка-
тегорий суть понятия “останется неизменным при перекладывании” заключается в том, что если мы возьмём
любую функцию к примеру прибавление единицы, то нам неважно когда её применять до функции onlyOne
или после. И в том и в другом случае мы получим одинаковый ответ. Давайте убедимся в этом:
onlyOne $ fmap (+1) [1,2,3,4,5]
=>
onlyOne [2,3,4,5,6]
=>
Just 2
fmap (+1) $ onlyOne [1,2,3,4,5]
=>
fmap (+1) $ Just 1
=>
Just 2
Результаты сошлись, обратите внимание на то, что функции fmap (+1) в двух вариантах являются раз-
ными функциями. Первая работает на списках, а вторая на частично определённых значениях. Суть в том,
что если при перекладывании значение не изменилось, то нам не важно когда выполнять преобразование
внутри функтора [] или внутри функтора Maybe. Теперь давайте выразим это на языке теории категорий.
Преобразование
для любого
Естественное преобразование | 231
Это свойство можно изобразить графически:
По смыслу ясно, что если у нас есть три структуры данных (или три функтора), если мы просто пере-
ложили данные из первой во вторую, а затем переложили данные из второй в третью, ничего не меняя. То
итоговое преобразование, которое составлено из последовательного применения перекладывания данных
также не меняет данные. Это говорит о том, что композиция двух естественных преобразований также явля-
ется естественным преобразованием. Также мы можем составить тождественное преобразование, для двух
одинаковых функторов
ся, что для двух категорий
функторы из
ния являются стрелками, которые соединяют функторы, мы будем обозначать их как обычные стрелки. Так
запись
Интересно, что изначально создатели теории категорий Саундедерс Маклейн и Сэмюэль Эйленберг при-
думали понятие естественного преобразования, а затем, чтобы дать ему обоснование было придумано поня-
тие функтора, и наконец для того чтобы дать обоснование функторам были придуманы категории. Катего-
рии содержат объекты и стрелки, для стрелок есть операция композиции. Также для каждого объекта есть
тождественная стрелка. Функторы являются стрелками в категории, в которой объектами являются другие
категории. А естественные преобразования являются стрелками в категории, в которой объектами являются
функторы. Получается такая иерархия структур.
15.4 Монады