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 Monad m 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
Так возможно совпадение не заметно, но давайте “перевернём” эту функцию:
(=<< )
:: Monad m => (a -> m b) -> m a -> m b
(=<< ) = flip (>>=)
Поменяв аргументы местами, мы получили знакомую функцию *$. Итак функция связывания это функция
применения специальной функции к специальному значению. У неё как раз такой смысл.
В Prelude определены экземпляры класса Monad для типов Maybe и [].
Они определены по такому же принципу, что и наши определения для Kleisli только не для композиции, а
для применения.
Отметим, что в модуле Control.Monad определены функции sequence и mapM, они несут тот же смысл,
что и функции sequence и mapК, которые мы определяли для класса Kleisli.
Свойства классов
Посмотрим на свойства функторов и аппликативных функторов.
Функторы и монады | 97
Свойства класса Functor
fmap id x
== x
— тождество
fmap f . fmap g
== fmap (f . g)
— композиция
Первое свойство говорит о том, что если мы применяем fmap к функции тождества, то мы должны снова
получить функцию тождества, или по другому можно сказать, что применение функции тождества к специ-
альному значению не изменяет это значение. Второе свойство говорит о том, что последовательное примене-
ние к специальному значению двух обычных функций можно записать в виде применения композиции двух
обычных функций к специальному значению.
Если всё это звучит туманно, попробуем переписать эти свойства в терминах композиции:
mf +> id
== mf
(mf +> g) +> h
== mf +> (g >> h)
Первое свойство говорит о том, что тождественная функция не изменяет значение при композиции. Вто-
рое свойство указывает на ассоциативность композиции одной специальной функции mf и двух обычных
функций g и h.
Свойства класса Applicative
Свойства класса Applicative, для наглядности они сформулированы не через методы класса, а через
производные функции.
fmap f x
== liftA f x
— связь с Functor
liftA
id x
== x
— тождество
liftA3 (. ) f g x
== f (g x)
— композиция
liftA
f (pure x)
== pure (f x)
— гомоморфизм
Первое свойство говорит о том, что применение специальной функции одного аргумента совпадает с
методом fmap из класса Functor. Свойство тождества идентично аналогичному свойству для класса Functor.
Свойство композиции сформулировано хитро, но давайте посмотрим на типы аргументов:
(. ) :: (b -> c) -> (a -> b) -> (a -> c)
f
:: m (b -> c)
g
:: m (a -> b)
x
:: m a
liftA3 (. ) f g x :: m c
g x
:: m b
f (g x)
:: m c
Слева в свойстве стоит liftA3, а не liftA2, потому что мы сначала применяем композицию (. ) к двум
функциям f и g, а затем применяем составную функцию к значению x.
Последнее свойство говорит о том, что если мы возьмём обычную функцию и обычное значение и подни-
мем их в мир специальных значений с помощью lift и pure, то это тоже самое если бы мы просто применили
бы функцию f к значению в мире обычных значений и затем подняли бы результат в мир специальных зна-
чений.
Полное определение классов
На самом деле я немного схитрил. Я рассказал вам только об основных методах классов Applicative
и Monad. Но они содержат ещё несколько дополнительных методов, которые выражаются через остальные.
Посмотрим на них, начнём с класса Applicative.
class Functor f => Applicative f where
— | Поднимаем значение в мир специальных значений.
pure :: a -> f a
— | Применение специального значения-функции.
( ) :: f (a -> b) -> f a -> f b
— | Константная функция. Отбрасываем первое значение.
98 | Глава 6: Функторы и монады: теория
(*> ) :: f a -> f b -> f b
(*> ) = liftA2 (const id)
— | Константная функция, Отбрасываем второе значение.
(<*) :: f a -> f b -> f a
(<*) = liftA2 const
Два новых метода (*> ) и (<*) имеют смысл константных функций. Первая функция игнорирует значение
слева, а вторая функция игнорирует значение справа. Посмотрим как они работают в интерпретаторе:
Prelude Control.Applicative> Just 2 *> Just 3
Just 3
Prelude Control.Applicative> Nothing *> Just 3
Nothing
Prelude Control.Applicative> (const id) Nothing
Just 3
Just 3
Prelude Control.Applicative> [1,2] <* [1,2,3]
[1,1,1,2,2,2]
Значение игнорируется, но способ комбинирования специальных функций учитывается. Так во втором
выражении не смотря на то, что мы не учитываем конкретное значение Nothing, мы учитываем, что если один