экспортирует тип >Writer w a
со своим экземпляром класса >Monad
и некоторые полезные функции для работы со значениями такого типа.
Прежде всего, давайте исследуем сам тип. Для присоединения моноида к значению нам достаточно поместить их в один кортеж. Тип >Writer w a
является просто обёрткой >newtype
для кортежа. Его определение несложно:
>newtype Writer w a = Writer { runWriter :: (a, w) }
Чтобы кортеж мог быть сделан экземпляром класса >Monad
и его тип был отделён от обычного кортежа, он обёрнут в >newtype
. Параметр типа >a
представляет тип значения, параметр типа >w
– тип присоединённого значения моноида.
Экземпляр класса >Monad
для этого типа определён следующим образом:
>instance (Monoid w) => Monad (Writer w) where
> return x = Writer (x, mempty)
> (Writer (x,v)) >>= f = let (Writer (y, v')) = f x
> in Writer (y, v `mappend` v')
Во-первых, давайте рассмотрим операцию >>>=
. Её реализация по существу аналогична функции >applyLog
, только теперь, поскольку наш кортеж обёрнут в тип >newtype Writer
, мы должны развернуть его перед сопоставлением с образцом. Мы берём значение >x
и применяем к нему функцию >f
. Это даёт нам новое значение >Writer
>w
>a
, и мы используем выражение >let
для сопоставления его с образцом. Представляем >y
в качестве нового результата и используем функцию >mappend
для объединения старого моноидного значения с новым. Упаковываем его вместе с результирующим значением в кортеж, а затем оборачиваем с помощью конструктора >Writer
, чтобы нашим результатом было значение >Writer
, а не просто необёрнутый кортеж.
Ладно, а что у нас с функцией >return
? Она должна принимать значение и помещать его в минимальный контекст, который по-прежнему возвращает это значение в качестве результата. Так каким был бы контекст для значений типа >Writer
? Если мы хотим, чтобы сопутствующее моноидное значение оказывало на другие моноидные значения наименьшее влияние, имеет смысл использовать функцию >mempty
. Функция >mempty
используется для представления «единичных» моноидных значений, как, например, >""
, >Sum
>0
и пустые строки байтов. Когда мы выполняем вызов функции >mappend
между значением >mempty
и каким-либо другим моноидным значением, результатом будет это второе моноидное значение. Так что если мы используем функцию >return
для создания значения монады >Writer
, а затем применяем оператор >>>=
для передачи этого значения функции, окончательным моноидным значением будет только то, что возвращает функция. Давайте используем функцию >return
с числом >3
несколько раз, только каждый раз будем соединять его попарно с другим моноидом: