В этом разделе мы изучим несколько функций, которые работают с монадическими значениями либо возвращают монадические значения в качестве своих результатов (или и то, и другое!). Такие функции обычно называют монадическими. В то время как некоторые из них будут для вас совершенно новыми, другие являются монадическими аналогами функций, с которыми вы уже знакомы – например, >filter
и >foldl
. Ниже мы рассмотрим функции >liftM
, >join
, >filterM
и >foldM
.
Когда мы начали своё путешествие на верхушку Горы Монад, мы сначала посмотрели на функторы, предназначенные для сущностей, которые можно отображать. Затем рассмотрели улучшенные функторы – аппликативные, которые позволяют нам применять обычные функции между несколькими аппликативными значениями, а также брать обычное значение и помещать его в некоторый контекст по умолчанию. Наконец, мы ввели монады как улучшенные аппликативные функторы, которые добавляют возможность тем или иным образом передавать эти значения с контекстом в обычные функции.
Итак, каждая монада – это аппликативный функтор, а каждый аппликативный функтор – это функтор. Класс типов >Applicative
имеет такое ограничение класса, ввиду которого наш тип должен иметь экземпляр класса >Functor
, прежде чем мы сможем сделать для него экземпляр класса >Applicative
. Класс >Monad
должен иметь то же самое ограничение для класса >Applicative
, поскольку каждая монада является аппликативным функтором – однако не имеет, потому что класс типов >Monad
был введён в язык Haskell задолго до класса >Applicative
.
Но хотя каждая монада – функтор, нам не нужно полагаться на то, что у неё есть экземпляр для класса >Functor
, в силу наличия функции >liftM
. Функция >liftM
берёт функцию и монадическое значение и отображает монадическое значение с помощью функции. Это почти одно и то же, что и функция >fmap
! Вот тип функции >liftM
:
>liftM :: (Monad m) => (a –> b) –> m a –> m b
Сравните с типом функции >fmap
:
>fmap :: (Functor f) => (a –> b) –> f a –> f b
Если экземпляры классов >Functor
и >Monad
для типа подчиняются законам функторов и монад, между этими двумя нет никакой разницы (и все монады, которые мы до сих пор встречали, подчиняются обоим). Это примерно как функции >pure
и >return
, делающие одно и то же, – только одна имеет ограничение класса >Applicative
, тогда как другая имеет ограничение >Monad
.
Давайте опробуем функцию >liftM
:
>ghci> liftM (*3) (Just 8)
>Just 24
>ghci> fmap (*3) (Just 8)
>Just 24
>ghci> runWriter $ liftM not $ Writer (True, "горох")
>(False,"горох")