Изучай Haskell во имя добра! (Липовача) - страница 177

>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), но перед возвратом результата к этому результату будет применена функция