Изучай Haskell во имя добра! (Липовача) - страница 247

экспортирует тип >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 несколько раз, только каждый раз будем соединять его попарно с другим моноидом: