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

>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, операция ><*> тоже может быть реализована, используя лишь то, что даёт нам класс типов