Читаем Изучай Haskell во имя добра! полностью

Теперь, когда мы увидели, что значение с присоединённым моноидом ведёт себя как монадическое значение, давайте исследуем экземпляр класса Monad для типов таких значений. Модуль Control.Monad.Writer экспортирует тип 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 несколько раз, только каждый раз будем соединять его попарно с другим моноидом:

ghci> runWriter (return 3 :: Writer String Int)

(3,"")

ghci> runWriter (return 3 :: Writer (Sum Int) Int)

(3,Sum {getSum = 0})

ghci> runWriter (return 3 :: Writer (Product Int) Int)

(3,Product {getProduct = 1})

Поскольку у типа Writer нет экземпляра класса Show, нам пришлось использовать функцию runWriter для преобразования наших значений типа Writer в нормальные кортежи, которые могут быть показаны в виде строки. Для строк единичным значением является пустая строка. Для типа Sum это значение 0, потому что если мы прибавляем к чему-то 0, это что-то не изменяется. Для типа Product единичным значением является 1.

В экземпляре класса Monad для типа Writer не имеется реализация для функции fail; значит, если сопоставление с образцом в нотации do оканчивается неудачно, вызывается функция error.

<p>Использование нотации do с типом Writer</p>

Теперь, когда у нас есть экземпляр класса Monad, мы свободно можем использовать нотацию do для значений типа Writer. Это удобно, когда у нас есть несколько значений типа Writer и мы хотим с ними что-либо делать. Как и в случае с другими монадами, можно обрабатывать их как нормальные значения, и контекст сохраняется для нас. В этом случае все моноидные значения, которые идут в присоединённом виде, объединяются с помощью функции mappend, а потому отражаются в окончательном результате. Вот простой пример использования нотации do с типом Writer для умножения двух чисел:

import Control.Monad.Writer

logNumber :: Int –> Writer [String] Int

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных