52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 254

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 254

Отметим, что бывают такие области, в которых стрелки или преобразования с одинаковыми именами

могут соединять несколько разных объектов. Например в Haskell есть классы, поэтому функции с одними

и теми же именами могут соединять разные объекты. Если все условия категории для объектов и стрелок

выполнены, кроме этого, то такую систему называют прекатегорией (pre-category). Из любой прекатегории

не сложно сделать категорию, если включить имена объектов в имя стрелки. Тогда у каждой стрелки будут

только одна пара объектов, которые она соединяет.

15.2 Функтор

Вспомним определение класса Functor:

class Functor f 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