>r
как
>(r
>–>)
.
Каким же образом функции выступают в качестве функторов? Давайте взглянем на реализацию, которая находится в модуле >Control.Monad.Instances
.
>instance Functor ((–>) r) where
> fmap f g = (\x –> f (g x))
Сначала подумаем над типом метода >fmap
:
>fmap :: (a –> b) –> f a –> f b
Далее мысленно заменим каждое вхождение идентификатора >f
, являющегося ролью, которую играет наш экземпляр функтора, выражением >(–>) r
. Это позволит нам понять, как функция >fmap
должна вести себя в отношении данного конкретного экземпляра. Вот результат:
>fmap :: (a –> b) –> ((–>) r a) –> ((–>) r b)
Теперь можно записать типы >(–>) r a
и >(–>) r b
в инфиксном виде, то есть >r
>–>
>a
и >r
>–>
>b
, как мы обычно поступаем с функциями:
>fmap :: (a –> b) –> (r –> a) –> (r –> b)
Хорошо. Отображение одной функции с помощью другой должно произвести функцию, так же как отображение типа >Maybe
с помощью функции должно произвести тип >Maybe
, а отображение списка с помощью функции – список. О чём говорит нам предыдущий тип? Мы видим, что он берёт функцию из >a
в >b
и функцию из >r
в >a
и возвращает функцию из >r
в >b
. Напоминает ли это вам что-нибудь? Да, композицию функций!.. Мы присоединяем выход >r –> a
ко входу >a –> b
, чтобы получить функцию >r –> b
, чем в точности и является композиция функций. Вот ещё один способ записи этого экземпляра:
>instance Functor ((–>) r) where
> fmap = (.)
Код наглядно показывает, что применение функции >fmap
к функциям – это просто композиция функций.
В исходном коде импортируйте модуль >Control.Monad.Instances
, поскольку это модуль, где определён данный экземпляр, а затем загрузите исходный код и попробуйте поиграть с отображением функций:
>ghci> :t fmap (*3) (+100)
>fmap (*3) (+100) :: (Num a) => a –> a
>ghci> fmap (*3) (+100) 1
>303
>ghci> (*3) `fmap` (+100) $ 1
>303
>ghci> (*3) . (+100) $ 1
>303
>ghci> fmap (show . (*3)) (*100) 1
>"300"
Мы можем вызывать >fmap
как инфиксную функцию, чтобы сходство с оператором >.
было явным. Во второй строке ввода мы отображаем >(+100)
с помощью >(*3)
, что даёт функцию, которая примет ввод, применит к нему >(+100)
, а затем применит к этому результату >(*3
). Затем мы применяем эту функцию к значению >1
.
Как и все функторы, функции могут восприниматься как значения с контекстами. Когда у нас есть функция вроде >(+3)
, мы можем рассматривать значение как окончательный результат функции, а контекстом является то, что мы должны применить эту функцию к чему-либо, чтобы получить результат. Применение >fmap (*3)
к >(+100)
создаст ещё одну функцию, которая действует так же, как >(+100)
, но перед возвратом результата к этому результату будет применена функция