>ghci> fmap (++ " ПРИВЕТ, Я ВНУТРИ JUST") Nothing
>Nothing
>ghci> fmap (*2) (Just 200)
>Just 400
>ghci> fmap (*2) Nothing
>Nothing
Деревья тоже являются функторами
Ещё один тип, который можно отображать и сделать для него экземпляр класса >Functor
, – это наш тип >Tree
. Дерево может хранить ноль или более других элементов, и конструктор типа >Tree
принимает один тип-параметр. Если бы мы хотели записать функцию >fmap
только для типа >Tree
, её сигнатура выглядела бы так: >(a
>–>
>b)
>–>
>Tree
>a
>–>
>Tree
>b
.
Для этой функции нам потребуется рекурсия. Отображение пустого дерева возвращает пустое дерево. Отображение непустого дерева – это дерево, состоящее из результата применения функции к корневому элементу и из правого и левого поддеревьев, к которым также было применено отображение.
>instance Functor Tree where
> fmap f EmptyTree = EmptyTree
> fmap f (Node x left right) = Node (f x) (fmap f left) (fmap f right)
Проверим:
>ghci> fmap (*2) EmptyTree
>EmptyTree
>ghci> fmap (*4) (foldr treeInsert EmptyTree [5,7,3])
>Node 20 (Node 12 EmptyTree EmptyTree) (Node 28 EmptyTree EmptyTree)
Впрочем, тут следует быть внимательным! Если тип >Tree
используется для представления бинарного дерева поиска, то нет никакой гарантии, что дерево останется таковым после применения к каждому его узлу некоторой функции. Проход по дереву функцией, скажем, >negate
превратит дерево поиска в обычное дерево.
И тип Either является функтором
Отлично! Ну а теперь как насчёт >Either a b
? Можно ли сделать его функтором? Класс типов >Functor
требует конструктор типов с одним параметром, а у типа >Either
их два. Гм-м… Придумал – мы частично применим конструктор >Either
, «скормив» ему один параметр, и таким образом он получит один свободный параметр. Вот как для типа >Either
определён экземпляр класса >Functor
в стандартных библиотеках:
>instance Functor (Either a) where
> fmap f (Right x) = Right (f x)
> fmap f (Left x) = Left x
Что же здесь происходит? Как видно из записи, мы сделали экземпляр класса не для типа >Either
, а для >Either a
. Это потому, что >Either
– конструктор типа, который принимает два параметра, а >Either a
– только один. Если бы функция >fmap
была только для >Either
>a
, сигнатура типа выглядела бы следующим образом:
>(b –> c) –> Either a b –> Either a c
поскольку это то же самое, что
>(b –> c) –> (Either a) b –> (Either a) c
В реализации мы выполняем отображение в конструкторе данных >Right
, но не делаем этого в >Left
. Почему? Вспомним, как определён тип >Either
>a
>b
:
>data Either a b = Left a | Right b
Если мы хотим применять некую функцию к обеим альтернативам, параметры