mapK :: Kleislim =>(a ->m b) ->[a] ->m [b]
mapK f =sequence .map f
6.4 Функторы и монады
В этой главе мы выписали вручную все определения для класса Kleisli. Мы сделали это потому, что на
самом деле в арсенале стандартных средств Haskell такого класса нет. Класс Kleisliстроит замкнутый мир
специальных функций a ->m b. Его цель построить язык в языке и сделать программирование со специ-
альными функциями таким же удобным как и с обычными функциями. Мы пользовались классом Kleisli
исключительно в целях облегчения понимания этого мира. Впрочем никто не мешает нам определить этот
класс и пользоваться им в наших программах.
А пока посмотрим, что есть в Haskell и как это соотносится с тем, что мы уже увидели. С помощью класса
Kleisli
мы научились делать три различных операции применения:
Применение:
• обычных функций одного аргумента к специальным значениям (функция +$).
• обычных функций произвольного числа аргументов к специальным значениям (функции +$и $$)
• специальных функций к специальным значениям (функция *$).
В Haskell для решения этих задач предназначены три отдельных класса. Это функторы, аппликативные
функторы и монады.
Функторы
Посмотрим на определение класса Functor:
class Functorf where
fmap ::(a ->b) ->f a ->f b
Тип метода fmap совпадает с типом для функции +$:
( +$) :: Kleislim =>(a ->b) ->m a ->m b
Нам только нужно заменить m на f и зависимость от Kleisliна зависимость от Functor:
Итак в Haskell у нас есть базовая операция fmap применения обычной функции к значению из мира спе-
циальных функций. В модуле Control.Applicativeопределён инфиксный синоним <$>для этой функции.
96 | Глава 6: Функторы и монады: теория
Аппликативные функторы
Посмотрим на определение класса Applicative:
class Functorf => Applicativef where
pure
::a ->f a
( <*>)
::f (a ->b) ->f a ->f b
Если присмотреться к типам методов этого класса, то мы заметим, что это наши старые знакомые idK и
$$. Если для данного типа f определён экземпляр класса Applicative, то из контекста следует, что для него
также определён и экземпляр класса Functor.
Значит у нас есть функции fmap (или lift1) и <*>(или $$). С их помощью мы можем составить функции
liftN, которые поднимают обычные функции произвольного числа аргументов в мир специальных значений.
Класс Applicativeопределён в модуле Control.Applicative, там же мы сможем найти и функции liftA,
liftA2, liftA3 и символьный синоним <$>для функции fmap. Функции liftAn определены так:
liftA2 f a b
=f <$>a <*>b
liftA3 f a b c =f <$>a <*>b <*>c
Видно что эти определения с точностью до обозначений совпадают с теми, что мы уже писали для класса
Kleisli.
Монады
Посмотрим на определение класса Monad
class Monadm where
return ::a ->m a
( >>=)
::m a ->(a ->m b) ->m b
Присмотримся к типам методов этого класса:
return ::a ->m a
Их типа видно, что это ни что иное как функция idK. В классе Monadу неё точно такой же смысл. Теперь
функция >>=, она читается как функция связывания (bind).
( >>=)
::m a ->(a ->m b) ->m b
Так возможно совпадение не заметно, но давайте “перевернём” эту функцию:
( =<<)
:: Monadm =>(a ->m b) ->m a ->m b
( =<<) =flip ( >>=)
Поменяв аргументы местами, мы получили знакомую функцию *$. Итак функция связывания это функция
применения специальной функции к специальному значению. У неё как раз такой смысл.
В Preludeопределены экземпляры класса Monadдля типов Maybeи [].
Они определены по такому же принципу, что и наши определения для Kleisliтолько не для композиции, а
для применения.
Отметим, что в модуле Control.Monadопределены функции sequence и mapM, они несут тот же смысл,
Читать дальше