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

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

бесконечно много подарков, что поделать нам приходится сохранить первый попавшийся, отбросив осталь-

ные. Главное в этой аналогии это то, что мы ничего не меняем, а лишь перекладываем содержимое из одной

структуры в другую.

В Haskell это можно описать так:

onlyOne :: [a] -> Maybe a

onlyOne []

= Nothing

onlyOne (a:as)

= 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

Пусть даны две категории A и B и два функтора F, G : A → B. Преобразованием (transformation) в B из

F в G называют семейство стрелок ε:

εA : F A →B GA

для любого A из A

Рассмотрим преобразование onlyOne :: [a] -> Maybe a. Категории A и B в данном случае совпадают~–

это категория Hask. Функтор F – это список, а функтор G это Maybe. Преобразование onlyOne для каждого

объекта 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]

=>