Мы “спрячем” из Prelude
одноимённую функцию sequence. Посмотрим на примеры:*Kleisli>
sequence [Just 1, Just 2, Just 3]Just
[1,2,3]*Kleisli>
sequence [Just 1, Nothing, Just 3]Nothing
Во второй команде вся функция вернула Nothing
потому что при объединении списка встретилось зна-чение Nothing
, это равносильно тому, что мы объединяем в один список, значения полученные из функций,которые могут не вычислить результат. Поскольку значение одного из элементов не определено, весь список
не определён.
Посмотрим как работает эта функция на списках:
*Kleisli>
sequence [[1,2,3], [11,22]][[1,11],[1,22],[2,11],[2,22],[3,11],[3,22]]
Она составляет список всех комбинаций элементов из всех подсписков.
С помощью этой функции мы можем определить функцию mapK. Эта функция является аналогом обычной
функции map, но она применяет специальную функцию к списку значений.
mapK :: Kleisli
m => (a -> m b) -> [a] -> m [b]mapK f =
sequence . map f6.4 Функторы и монады
В этой главе мы выписали вручную все определения для класса Kleisli
. Мы сделали это потому, что насамом деле в арсенале стандартных средств Haskell такого класса нет. Класс Kleisli
строит замкнутый мирспециальных функций a ->
m b. Его цель построить язык в языке и сделать программирование со специ-альными функциями таким же удобным как и с обычными функциями. Мы пользовались классом Kleisli
исключительно в целях облегчения понимания этого мира. Впрочем никто не мешает нам определить этот
класс и пользоваться им в наших программах.
А пока посмотрим, что есть в Haskell и как это соотносится с тем, что мы уже увидели. С помощью класса
Kleisli
мы научились делать три различных операции применения:
Применение:
• обычных функций одного аргумента к специальным значениям (функция +$
).• обычных функций произвольного числа аргументов к специальным значениям (функции +$
и $$)• специальных функций к специальным значениям (функция *$
).В Haskell для решения этих задач предназначены три отдельных класса. Это функторы, аппликативные
функторы и монады.
Функторы
Посмотрим на определение класса Functor
:class Functor
f wherefmap ::
(a -> b) -> f a -> f bТип метода fmap совпадает с типом для функции +$
:(+$
) :: Kleisli m => (a -> b) -> m a -> m bНам только нужно заменить m на f и зависимость от Kleisli
на зависимость от Functor:Итак в Haskell у нас есть базовая операция fmap применения обычной функции к значению из мира спе-
циальных функций. В модуле Control.Applicative
определён инфиксный синоним <$> для этой функции.96 | Глава 6: Функторы и монады: теория
Аппликативные функторы
Посмотрим на определение класса Applicative
:class Functor
f => Applicative f wherepure
::
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 <*> bliftA3 f a b c =
f <$> a <*> b <*> cВидно что эти определения с точностью до обозначений совпадают с теми, что мы уже писали для класса
Kleisli
.Монады
Посмотрим на определение класса Monad
class Monad
m wherereturn ::
a -> m a(>>=
)::
m a -> (a -> m b) -> m bПрисмотримся к типам методов этого класса:
return ::
a -> m aИх типа видно, что это ни что иное как функция idK. В классе Monad
у неё точно такой же смысл. Теперьфункция >>=
, она читается как функция(>>=
)::
m a -> (a -> m b) -> m bТак возможно совпадение не заметно, но давайте “перевернём” эту функцию:
(=<<
):: Monad
m => (a -> m b) -> m a -> m b(=<<
) = flip (>>=)Поменяв аргументы местами, мы получили знакомую функцию *$
. Итак функция связывания это функцияприменения специальной функции к специальному значению. У неё как раз такой смысл.
В Prelude
определены экземпляры класса Monad для типов Maybe и [].Они определены по такому же принципу, что и наши определения для Kleisli
только не для композиции, адля применения.
Отметим, что в модуле Control.Monad
определены функции sequence и mapM, они несут тот же смысл,что и функции sequence и mapК
, которые мы определяли для класса Kleisli.Свойства классов
Посмотрим на свойства функторов и аппликативных функторов.
Функторы и монады | 97