Стандартные библиотеки устроены так, потому что класс Applicative
появился гораздо позже классаMonad
. И к появлению этого нового класса уже накопилось огромное число библиотек, которые рассчитанына прежние имена. Но в будущем возможно прежние классы будут заменены на такие классы:
class Functor
f wherefmap ::
(a -> b) -> f a -> f bclass Pointed
f wherepure ::
a -> f aclass
(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 aclass Applicative
f => Monad f where(>>=
) :: f a -> (a -> f b) -> f bФункторы и монады | 99
6.5 Краткое содержание
В этой главе мы долгой обходной дорогой шли к понятию монады и функтора. Эти классы служат для
облегчения работы в мире специальных функций вида a ->
m b, в категории КлейслиС помощью класса Functor
можно применять специальные значения к обычным функциям одного аргу-мента:
class Functor
f wherefmap ::
(a -> b) -> f a -> f bС помощью класса Applicative
можно применять специальные значения к обычным функциям любогочисла аргументов:
class Functor
f => Applicative f wherepure
::
a -> f a<*>
::
f (a -> b) -> f a -> f bliftA
:: Applicative
f => (a -> b) -> f a -> f bliftA2 :: Applicative
f => (a -> b -> c) -> f a -> f b -> f cliftA3 :: Applicative
f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d...
С помощью класса Monad
можно применять специальные значения к специальным функциям.class Monad
m wherereturn
::
a -> m a(>>=
)::
m a -> (a -> m b) -> m bФункция return является функцией id в мире специальных функций, а функция >>=
является функциейприменения ($
), с обратным порядком следования аргументов. Вспомним также класс Kleisli, на примерекотором мы узнали много нового из жизни специальных функций:
class Kleisli
m whereidK
::
a -> m a(*>
)::
(a -> m b) -> (b -> m c) -> (a -> m c)Мы узнали несколько стандартных специальных функций:
Частично определённые функции
a -> Maybe
bdata 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) = fb
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
, а для States, то есть мы свяжем тип с некоторым произвольным типом s.
instance Kleisli
(State s) where...
Упражнения | 101
a
f
b
env
Рис. 6.8: Функция с окружением
Функции с окружением
Сначала мы рассмотрим функции с окружением. Функции с окружением – это такие функции, у которых
есть некоторое хранилище данных или окружение, из которых они могут читать информацию. Но в отличие
от функций с состоянием они не могут это окружение изменять. Функция с окружением похожа на функцию
с состоянием без одного выхода для состояния (рис. 6.8).
Функция с окружением принимает аргумент a и окружение env и возвращает результат b:
a ->
env -> b