могут соединять несколько разных объектов. Например в Haskell есть классы, поэтому функции с одними
и теми же именами могут соединять разные объекты. Если все условия категории для объектов и стрелок
выполнены, кроме этого, то такую систему называют прекатегорией (pre-category). Из любой прекатегории
не сложно сделать категорию, если включить имена объектов в имя стрелки. Тогда у каждой стрелки будут
только одна пара объектов, которые она соединяет.
15.2 Функтор
Вспомним определение класса Functor:
class Functorf where
fmap ::(a ->b) ->(f a ->f b)
В этом определении участвуют тип f и метод fmap. Можно сказать, что тип f переводит произвольные
типы a в специальные типы f a. В этом смысле тип f является функцией, которая определена на типах. Метод
fmap переводит функции общего типа a ->b в специальные функции f a ->f b.
При этом должны выполняться свойства:
fmap id
=id
fmap (f .g) =fmap f .fmap g
Теперь вспомним о категории Hask. В этой категории объектами являются типы, а стрелками функции.
Функтор f отображает объекты и стрелки категории Haskв объекты и стрелки f Hask. При этом оказывается,
что за счёт свойств функтора f Haskобразует категорию.
• Объекты – это типы f a.
• Стрелки – это функции fmap f.
• Композиция стрелок это просто композиция функций.
• Тождественная стрелка это fmap id.
Проверим аксиомы:
fmap f .fmap id =fmap f .id =fmap f
fmap id .fmap f =id .fmap f =fmap f
fmap f .(fmap g .fmap h)
=
fmap f .fmap (g .h)
=
fmap (f .(g .h))
=
fmap ((f .g) .h)
=
fmap (f .g) .fmap h
=
(fmap f .fmap g) .fmap h
Функтор | 229
Видно, что аксиомы выполнены, так функтор f порождает категорию f Hask. Интересно, что поскольку
Haskсодержит все типы, то она содержит и типы f Hask. Получается, что мы построили категорию внутри
категории. Это можно пояснить на примере списков. Тип []погружает любой тип в список, а функцию для
любого типа можно превратить в функцию, которая работает на списках с помощью метода fmap. При этом с
помощью класса Functorмы проецируем все типы и все функции в мир списков [a]. Но сам этот мир списков
содержится в категории Hask.
С помощью функторов мы строим внутри одной категории другую категорию, при этом внутренняя ка-
тегория обладает некоторой структурой. Так если раньше у нас были только произвольные типы a и произ-
вольные функции a ->b, то теперь все объекты имеют тип [a] и все функции имеют тип [a] ->[b]. Также и
функтор Maybeпереводит произвольное значение, в значение, которое обладает определённой структурой. В
нём выделен дополнительный элемент Nothing, который обозначает отсутствие значения. Если по типу val
::a мы ничего не можем сказать о содержании значения val, то по типу val :: Maybea, мы знаем один
уровень конструкторов. Например мы уже можем проводить сопоставление с образцом.
Теперь давайте вернёмся к теории категорий и дадим формальное определение понятия. Пусть A и B –
категории, тогда функтором из A в B называют отображение F , которое переводит объекты A в объекты B
и стрелки A в стрелки B , так что выполнены следующие свойства:
F f
:
F A →B F B если f : A →A B
F idA
=
idF A
для любого объекта A из A
F ( f ; g )
=
F f ; F g
если ( f ; g ) подходят по типам
Здесь запись →A и →B означает, что эти стрелки в разных категориях. После отображения стрелки f
из категории A мы получаем стрелку в категории B , это и отражено в типе F f : F A →B F B . Первое
свойство говорит о том, что после отображения стрелки соединяют те же объекты, что и до отображения.
Второе свойства говорит о сохранении тождественных стрелок. А последнее свойство, говорит о том, что
“пути” между объектами также сохраняются. Если мы находимся в категории A в объекте A и перед нами
есть путь состоящий из нескольких стрелок в объект B , то неважно как мы пойдём в F B либо мы пройдём
Читать дальше