>ghci> runWriter $ fmap not $ Writer (True, "горох")
>(False,"горох")
>ghci> runState (liftM (+100) pop) [1,2,3,4]
>(101,[2,3,4])
>ghci> runState (fmap (+100) pop) [1,2,3,4]
>(101,[2,3,4])
Вы уже довольно хорошо знаете, как функция >fmap
работает со значениями типа >Maybe
. И функция >liftM
делает то же самое. При использовании со значениями типа >Writer
функция отображает первый компонент кортежа, который является результатом. Выполнение функций >fmap
или >liftM
с вычислением, имеющим состояние, даёт в результате другое вычисление с состоянием, но его окончательный результат изменяется добавленной функцией. Если бы мы не отобразили функцию >pop
с помощью >(+100)
перед тем, как выполнить её, она бы вернула >(1, [2,3,4])
.
Вот как реализована функция >liftM
:
>liftM :: (Monad m) => (a –> b) –> m a –> m b
>liftM f m = m >>= (\x –> return (f x))
Или с использованием нотации >do
:
>liftM :: (Monad m) => (a –> b) –> m a –> m b
>liftM f m = do
> x <– m
> return (f x)
Мы передаём монадическое значение >m
в функцию, а затем применяем функцию к его результату, прежде чем поместить его обратно в контекст по умолчанию. Ввиду монадических законов гарантируется, что функция не изменит контекст; она изменяет лишь результат, который представляет монадическое значение.
Вы видите, что функция >liftM
реализована совсем не ссылаясь на класс типов >Functor
. Значит, мы можем реализовать функцию >fmap
(или >liftM
– называйте, как пожелаете), используя лишь те блага, которые предоставляют нам монады. Благодаря этому можно заключить, что монады, по крайней мере, настолько же сильны, насколько и функторы.
Класс типов >Applicative
позволяет нам применять функции между значениями с контекстами, как если бы они были обычными значениями, вот так:
>ghci> (+) <$> Just 3 <*> Just 5
>Just 8
>ghci> (+) <$> Just 3 <*> Nothing
>Nothing
Использование этого аппликативного стиля всё упрощает. Операция ><$>
– это просто функция >fmap
, а операция ><*>
– это функция из класса типов >Applicative
, которая имеет следующий тип:
>(<*>) :: (Applicative f) => f (a –> b) –> f a –> f b
Так что это вроде >fmap
, только сама функция находится в контексте. Нам нужно каким-то образом извлечь её из контекста и с её помощью отобразить значение >f a
, а затем вновь собрать контекст. Поскольку все функции в языке Haskell по умолчанию каррированы, мы можем использовать сочетание из операций ><$>
и ><*>
между аппликативными значениями, чтобы применять функции, принимающие несколько параметров.
Однако, оказывается, как и функция >fmap
, операция ><*>
тоже может быть реализована, используя лишь то, что даёт нам класс типов