Выбрать главу

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 к значению в мире обычных значений и затем подняли бы результат в мир специальных зна-