Функторы в Haskell

На правах рекламы:
Туалетные кабины Санкт-Петербург

Функторы в Haskell является тем, чем и должны быть настоящие функторы. Функторы Haskell очень напоминают математические функторы из теории категорий. В теории категорий функтор F — это отображение между категориями, такое, что структура категории сохраняется или, другими словами, это гомоморфизм между двумя категориями.

В Haskell это определение реализовано в виде простого класса типа,

class Functor f where
  fmap :: (a -> b) -> f a -> f b


Оглядываясь, например, на ML, класс типа в Haskell подобен сигнатуре, за исключением того, что он определен для типа. Он определяет, какие операции должен реализовать тип, чтобы быть экземпляром данного класса. В данном случае, однако, Functor определен не над типами, а над конструктором типа f. Это значит, Functor — это то, что реализует функцию fmap, которая принимает функцию (принимающую тип a и возращающую тип b) и значение типа f a (тип построеный из конструктора типа f, применяемый к типу a) и возвращает значение типа f b.

Чтобы понять, что он делает, думайте о fmap как о функции, которая применяет операцию к каждому элементу в каком-то контейнере.

Простейший пример функторов — это обычные списки и функция map, которая применяет функцию к каждому элементу в списке.

Prelude> map (+1) [1,2,3,4,5]
[2,3,4,5,6]

В этом простом примере, функцией Functor'а fmap является просто map и конструктором типа f является [] — конструктор типа списка. Поэтому Functor, например, для списков определяется как

instance Functor [] where
  fmap = map

Давайте посмотрим, что это действительно верно, используя fmap вместо map,

Prelude> fmap (+1) [1,2,3,4,5]
[2,3,4,5,6]

Но заметьте, что определение Functor ничего не говорит о сохранении структуры! Поэтому любой нормальный функтор должен неявно удовлетворять законам функторов, которые являются частью определения математических функторов. Есть два правила fmap:

fmap id = id
fmap (g. h) = fmap g. fmap h

Первое правило гласит, что отображение тождественной функции на каждый элемент в контейнере не имеет никакого эффекта. Второе правило гласит, что композиция двух функций над каждым элементом в контейнере то же самое, что отображение первой функции, а затем отображение второй.
  • avatar
  • 0
  • 0