Стандартные библиотеки устроены так, потому что класс 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
С помощью класса Applicative можно применять специальные значения к обычным функциям любого
числа аргументов:
class Functor f => Applicative f where
pure
:: a -> f a
<*>
:: f (a -> b) -> f a -> f b
liftA
:: Applicative f => (a -> b) -> f a -> f b
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
...
С помощью класса Monad можно применять специальные значения к специальным функциям.
class Monad m where
return
:: a -> m a
(>>=)
:: m a -> (a -> m b) -> m b
Функция return является функцией id в мире специальных функций, а функция >>= является функцией
применения ($), с обратным порядком следования аргументов. Вспомним также класс Kleisli, на примере
котором мы узнали много нового из жизни специальных функций:
class Kleisli m where
idK
:: a -> m a
(*> )
:: (a -> m b) -> (b -> m c) -> (a -> m c)
Мы узнали несколько стандартных специальных функций:
Частично определённые функции
a -> Maybe b
data Maybe a = Nothing | Just a
Многозначные функции
a -> [b]
data [a] = [] | a : [a]
6.6 Упражнения
В первых упражнениях вам предлагается по картинке специальной функции написать экземпляр классов
Kleisli и Monad.
Функции с состоянием
b
a
f
s
s
Рис. 6.6: Функция с состоянием
100 | Глава 6: Функторы и монады: теория
В Haskell нельзя изменять значения. Новые сложные значения описываются в терминах базовых значе-
ний. Но как же тогда мы сможем описать функцию с состоянием? Функцию, которая принимает на вход
значение, составляет результат на основе внутреннего состояния и значения аргумента и обновляет состоя-
ние. Поскольку мы не можем изменять состояние единственное, что нам остаётся – это принимать значение
состояния на вход вместе с аргументом и возвращать обновлённое состояние на выходе. У нас получится
такой тип:
a -> s -> (b, s)
Функция принимает одно значение типа a и состояние типа s, а возвращает пару, которая состоит из
результата типа b и обновлённого состояния. Если мы введём синоним:
type State s b = s -> (b, s)
И вспомним о частичном применении, то мы сможем записать тип функции с состоянием так:
a -> State s b
В Haskell пошли дальше и выделили для таких функций специальный тип:
data State s a = State (s -> (a, s))
runState :: State s a -> s -> (a, s)
runState (State f) = f
b
c
a
f
b
g
s
s
s
s
b
c
a
g
f
s
s
s
c
a
f*>g
s
s
Рис. 6.7: Композиция функций с состоянием
Функция runState просто извлекает функцию из оболочки State.
На (рис. 6.6) изображена схема функции с состоянием. В сравнении с обычной функцией у такой функции
один дополнительный выход и один дополнительный вход типа s. По ним течёт и изменяется состояние.
Попробуйте по схеме композиции для функций с состоянием написать экземпляры для классов Kleisli
и Monad для типа State s (рис. 6.7).
Подсказка: В этом определении есть одна хитрость, в отличае от типов Maybe и [a] у типа State два
параметра, это параметр состояния и параметр значения. Но мы делаем экземпляр не для State, а для State
s, то есть мы свяжем тип с некоторым произвольным типом s.
instance Kleisli (State s) where
...
Упражнения | 101
a
f
b
env
Рис. 6.8: Функция с окружением
Функции с окружением
Сначала мы рассмотрим функции с окружением. Функции с окружением – это такие функции, у которых
есть некоторое хранилище данных или окружение, из которых они могут читать информацию. Но в отличие
от функций с состоянием они не могут это окружение изменять. Функция с окружением похожа на функцию
с состоянием без одного выхода для состояния (рис. 6.8).
Функция с окружением принимает аргумент a и окружение env и возвращает результат b:
a -> env -> b