этот путь в категории A и в самом конце переместимся в F B или мы сначала переместимся в F A и затем
пройдём по образу пути в категории F B . Так и так мы попадём в одно и то же место. Схематически это
можно изобразить так:
f
g
A
B
C
F
F
F
F A
F B
F C
F f
F g
Стрелки сверху находятся в категории A , а стрелки снизу находятся в категории B . Функтор F : A → A ,
который переводит категорию A в себя называют эндофунктором (endofunctor). Функторы отображают одни
категории в другие сохраняя структуру первой категории. Мы словно говорим, что внутри второй категории
есть структура подобная первой. Интересно, что последовательное применение функторов, также является
функтором. Мы будем писать последовательное применение функторов F и G слитно, как F G . Также можно
определить и тождественный функтор, который ничего не делает с категорией, мы будем обозначать его как
IA или просто I , если категория на которой он определён понятна из контекста. Это говорит о том, что мы
можем построить категорию, в которой объектами будут другие категории, а стрелками будут функторы.
15.3 Естественное преобразование
В программировании часто приходится переводить данные из одной структуры в другую. Каждая из
структур хранит какие-то конкретные значения, но мы ничего с ними не делаем мы просто перекладываем
содержимое из одного ящика в другой. Например в нашем ящике только один отсек, но вдруг нам пришло
бесконечно много подарков, что поделать нам приходится сохранить первый попавшийся, отбросив осталь-
ные. Главное в этой аналогии это то, что мы ничего не меняем, а лишь перекладываем содержимое из одной
структуры в другую.
В Haskell это можно описать так:
onlyOne ::[a] -> Maybea
onlyOne []
= Nothing
onlyOne (a :as)
= Justa
В этой функции мы перекладываем элементы из списка [a] в частично определённое значение Maybe.
Тоже самое происходит и в функции concat:
230 | Глава 15: Теория категорий
concat ::[[a]] ->[a]
Элементы перекладываются из списка списков в один список. В теории категорий этот процесс называ-
ется естественным преобразованием. Структуры определяются функторами. Поэтому в определении будет
участвовать два функтора. В функции onlyOne это были функторы []и Maybe. При перекладывании элемен-
тов мы можем просто выбросить все элементы:
burnThemALl ::[a] ->()
burnThemAll =const ()
Можно сказать, что единичный тип также определяет функтор. Это константный функтор, он переводит
любой тип в единственное значение (), а функцию в id:
data Emptya = Empty
instance Functor Empty where
fmap =const id
Тогда тип функции burnThemAll будет параметризован и слева и справа от стрелки:
burnThemAll ::[a] -> Emptya
burnThemAll =const Empty
Пусть даны две категории A и B и два функтора F, G : A → B . Преобразованием (transformation) в B из
F в G называют семейство стрелок ε :
εA : F A →B GA
для любого A из A
Рассмотрим преобразование onlyOne ::[a] -> Maybea. Категории A и B в данном случае совпадают~–
это категория Hask. Функтор F – это список, а функтор G это Maybe. Преобразование onlyOne для каждого
объекта a из Haskопределяет стрелку
onlyOne ::[a] -> Maybea
Так мы получаем семейство стрелок, параметризованное объектом из Hask:
onlyOne ::[ Int] -> Maybe Int
onlyOne ::[ Char] -> Maybe Char
onlyOne ::[ Int -> Int] -> Maybe( Int -> Int)
...
...
Теперь давайте определим, что значит перекладывать из одной структуры в другую, не меняя содержа-
ния. Представим, что функтор – это контейнер. Мы можем менять его содержание с помощью метода fmap.
Например мы можем прибавить единицу ко всем элементам списка xs с помощью выражения fmap ( +1) xs.
Точно так же мы можем прибавить единицу к частично определённому значению. С точки зрения теории ка-
тегорий суть понятия “останется неизменным при перекладывании” заключается в том, что если мы возьмём
любую функцию к примеру прибавление единицы, то нам неважно когда её применять до функции onlyOne
Читать дальше