Kleisliи Monad.
Функции с состоянием
b
a
f
s
s
Рис. 6.6: Функция с состоянием
100 | Глава 6: Функторы и монады: теория
В Haskell нельзя изменять значения. Новые сложные значения описываются в терминах базовых значе-
ний. Но как же тогда мы сможем описать функцию с состоянием? Функцию, которая принимает на вход
значение, составляет результат на основе внутреннего состояния и значения аргумента и обновляет состоя-
ние. Поскольку мы не можем изменять состояние единственное, что нам остаётся – это принимать значение
состояния на вход вместе с аргументом и возвращать обновлённое состояние на выходе. У нас получится
такой тип:
a ->s ->(b, s)
Функция принимает одно значение типа a и состояние типа s, а возвращает пару, которая состоит из
результата типа b и обновлённого состояния. Если мы введём синоним:
type States b =s ->(b, s)
И вспомним о частичном применении, то мы сможем записать тип функции с состоянием так:
a -> States b
В Haskell пошли дальше и выделили для таких функций специальный тип:
data States a = State(s ->(a, s))
runState :: States a ->s ->(a, s)
runState ( Statef) =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для типа States (рис. 6.7).
Подсказка: В этом определении есть одна хитрость, в отличае от типов Maybeи [a] у типа Stateдва
параметра, это параметр состояния и параметр значения. Но мы делаем экземпляр не для State, а для State
s, то есть мы свяжем тип с некоторым произвольным типом s.
instance Kleisli( States) where
...
Упражнения | 101
a
f
b
env
Рис. 6.8: Функция с окружением
Функции с окружением
Сначала мы рассмотрим функции с окружением. Функции с окружением – это такие функции, у которых
есть некоторое хранилище данных или окружение, из которых они могут читать информацию. Но в отличие
от функций с состоянием они не могут это окружение изменять. Функция с окружением похожа на функцию
с состоянием без одного выхода для состояния (рис. 6.8).
Функция с окружением принимает аргумент a и окружение env и возвращает результат b:
a ->env ->b
Как и в случае функций с состоянием выделим для функции с окружением отдельный тип. В Haskell он на-
зывается Reader(от англ. чтец). Все функции с окружением имеют возможность читать из общего хранилища
данных. Например они могут иметь доступ на чтение к общей базе данных.
data Readerenv b = Reader(env ->b)
runReader :: Readerenv b ->(env ->b)
runReader ( Readerf) =f
Теперь функция с окружением примет вид:
a -> Readerenv b
Определите для функций с окружением экземпляр класса Kleisli. У нас возникнет цепочка функций,
каждая из которых будет нуждаться в значении окружения. Поскольку окружение общее для всех функций
мы всем функциям передадим одно и то же значение (рис. 6.9).
a
f
b
b
g
c
env
env
b
a
g
f
c
env
a
f*>g
c
env
Рис. 6.9: Функция с окружением
Функции-накопители
Функции-накопители при вычислении за ширмой накапливают некоторое значение. Функция-накопитель
похожа на функцию с состоянием но без стрелки, по которой состояние подаётся в функцию (рис. 6.10).
Функция-накопитель имеет тип: a ->(b, msg)
Выделим результат функции в отдельный тип с именем Writer.
102 | Глава 6: Функторы и монады: теория
a
f
b
Msg
Рис. 6.10: Функция-накопитель
data Writermsg b = Writer(b, msg)
runWriter :: Writermsg b ->(b, msg)
runWriter ( Writera) =a
Тип функции примет вид:
a -> Writermsg b
Значения типа msg мы будем называть сообщениями. Смысл функций a -> Writermsg b заключается
в том, что при вычислении они накапливают в значении msg какую-нибудь информацию. Это могут быть
Читать дальше