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

чений.

Полное определение классов

На самом деле я немного схитрил. Я рассказал вам только об основных методах классов 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, мы учитываем, что если один

из аргументов частично определённой функции не определён, то не определено всё значение. Сравните с

результатом выполнения следующего выражения.

По той же причине в последнем выражении мы получили три копии первого списка. Так произошло

потому, что второй список содержал три элемента. К каждому из элементов была применена функция const

x, где x пробегает по элементам списка слева от (<*).

Аналогичный метод есть и в классе Monad:

class

Monad m

where

return

:: a -> m a

(>>=)

:: m a -> (a -> m b) -> m b

(>> )

:: m a -> m b -> m b

fail

:: String -> m a

m >> k

= m >>= const k

fail s

= error s

Функция >> в классе Monad, которую мы прятали из-за символа композиции, является аналогом постоян-

ной функции в классе Monad. Она работает так же как и *> . Функция fail используется для служебных нужд

Haskell при выводе ошибок. Поэтому мы её здесь не рассматриваем. Для определения экземпляра класса

Monad достаточно определить методы return и >>=.

Исторические замечания

Напрашивается вопрос. Зачем нам функции return и pure или *> и >> ? Если вы заглянете в документа-

цию к модулю Control.Monad, то там вы найдёте функции liftM, liftM2, liftM3, которые выполняют те же

операции, что и аналогичные функции из модуля Control.Applicative.

Стандартные библиотеки устроены так, потому что класс Applicative появился гораздо позже класса

Monad. И к появлению этого нового класса уже накопилось огромное число библиотек, которые рассчитаны

на прежние имена. Но в будущем возможно прежние классы будут заменены на такие классы:

class Functor f where

fmap :: (a -> b) -> f a -> f b

class Pointed f where

pure :: a -> f a

class (Functor f, Pointed f) => Applicative f where

(<*> ) :: f (a -> b) -> f a -> f b

(*> )

:: f a -> f b -> f b

(<*)

:: f a -> f b -> f a

class Applicative f => Monad f where

(>>=) :: f a -> (a -> f b) -> f b

Функторы и монады | 99

6.5 Краткое содержание

В этой главе мы долгой обходной дорогой шли к понятию монады и функтора. Эти классы служат для

облегчения работы в мире специальных функций вида a -> m b, в категории Клейсли

С помощью класса Functor можно применять специальные значения к обычным функциям одного аргу-

мента:

class Functor f where

fmap :: (a -> b) -> f a -> f b