Помимо простых типов данных есть более сложные конструкции: функции над типами или конструкторы типов с параметрами.
Для функций над типами тоже можно задавать общий интерфейс с помощью механизма класса типов. Определение класса типов Functor:
Класс типов Functor параметризован переменной f |
f используется не как тип, а как некоторая функция над типами. Из применения переменной f к a и b следует, что у нее должен быть стрелочный kind: * -> *. После того как мы применяем ее к некоторому типу получившаяся конструкция будет иметь kind: *.
Функция fmap принимает некоторую произвольную функцию. Второе, что она принимает, это некоторый контейнерный тип с типом a для элементов этого контейнера. Ну например вместо f подставляется список или Maybe. Возвращает она тот же самый контейнерный тип, но с типом элементов b.
Functor для списков полностью повторяет map.
Prelude> :k []
[] :: * -> *
Prelude> map succ [1,2,3]
[2,3,4]
Prelude> fmap succ [1,2,3]
[2,3,4]
Класс типов Functor поднимает вычисления в контейнер. Поднимает некоторую функцию в контейнер. Он принимает в качестве аргумента некоторую произвольную функцию, а возвращает некоторую функцию над контейнерами:
fmap :: (a -> b) -> (f a -> f b)
Поднять вычисления, означает превратить вычисление из преобразующего тип данных a в тип данных b получить вычисление преобразующее тип данных a упакованный в некоторый контейнер в тип данных b упакованный в тот же самый контейнер.
Реализация представителя Functor-а для типа данных Maybe:
Prelude> fmap (*2) Nothing
Nothing
Prelude> fmap (*2) (Just 21)
Just 42
Примеры
Определение представителя класса Functor для типа данных, представляющего точку в трёхмерном пространстве:
data Point3D a = Point3D a a a deriving Show
instance Functor Point3D where
fmap f (Point3D a b c) = Point3D (f a) (f b) (f c)
GHCi> fmap (+ 1) (Point3D 5 6 7)
Point3D 6 7 8
Определение представителя класса Functor для типа данных GeomPrimitive:
data GeomPrimitive a = Point (Point3D a) | LineSegment (Point3D a) (Point3D a)
instance Functor GeomPrimitive where
fmap f (Point a) = Point (fmap f a)
fmap f (LineSegment a b) = LineSegment (fmap f a) (fmap f b)
GHCi> fmap (+ 1) $ Point (Point3D 0 0 0)
Point (Point3D 1 1 1)
GHCi> fmap (+ 1) $ LineSegment (Point3D 0 0 0) (Point3D 1 1 1)
LineSegment (Point3D 1 1 1) (Point3D 2 2 2)
Point (Point3D 1 1 1)
GHCi> fmap (+ 1) $ LineSegment (Point3D 0 0 0) (Point3D 1 1 1)
LineSegment (Point3D 1 1 1) (Point3D 2 2 2)
Определение представителя класса Functor для бинарного дерева, в каждом узле которого хранятся элементы типа Maybe:
data Tree a = Leaf (Maybe a) | Branch (Tree a) (Maybe a) (Tree a) deriving Show
data Tree a = Leaf (Maybe a) | Branch (Tree a) (Maybe a) (Tree a) deriving Show
instance Functor Tree where
fmap f (Leaf l) = Leaf (fmap f l)
fmap f (Branch n1 v n2) = Branch (fmap f n1) (fmap f v) (fmap f n2)
GHCi> words <$> Leaf Nothing
Leaf Nothing
GHCi> words <$> Leaf (Just "a b")
Leaf (Just ["a","b"])