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

Некоторые полезные монадические функции

В этом разделе мы изучим несколько функций, которые работают с монадическими значениями либо возвращают монадические значения в качестве своих результатов (или и то, и другое!). Такие функции обычно называют монадическими. В то время как некоторые из них будут для вас совершенно новыми, другие являются монадическими аналогами функций, с которыми вы уже знакомы – например, >filter и >foldl. Ниже мы рассмотрим функции >liftM, >join, >filterM и >foldM.


liftM и компания

Когда мы начали своё путешествие на верхушку Горы Монад, мы сначала посмотрели на функторы, предназначенные для сущностей, которые можно отображать. Затем рассмотрели улучшенные функторы – аппликативные, которые позволяют нам применять обычные функции между несколькими аппликативными значениями, а также брать обычное значение и помещать его в некоторый контекст по умолчанию. Наконец, мы ввели монады как улучшенные аппликативные функторы, которые добавляют возможность тем или иным образом передавать эти значения с контекстом в обычные функции.

Итак, каждая монада – это аппликативный функтор, а каждый аппликативный функтор – это функтор. Класс типов >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,"горох")