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

между всеми смежными списками, содержащимися в списке.

Законы моноидов действительно выполняются для экземпляра списка. Когда у нас есть несколько списков и мы объединяем их с помощью функции >mappend (или >++), не имеет значения, какие списки мы соединяем первыми, поскольку так или иначе они соединяются на концах. Кроме того, пустой список действует как единица, поэтому всё хорошо.

Обратите внимание, что моноиды не требуют, чтобы результат выражения >a `mappend` b был равен результату выражения >b `mappend` a. В случае со списками они очевидно не равны:

>ghci> "один" `mappend` "два"

>"одиндва"

>ghci> "два" `mappend` "один"

>"дваодин"

И это нормально. Тот факт, что при умножении выражения >3 * 5 и >5 * 3 дают один и тот же результат, – это просто свойство умножения, но оно не выполняется для большинства моноидов.

Типы Product и Sum

Мы уже изучили один из способов рассматривать числа как моноиды: просто позволить бинарной функции быть оператором >*, а единичному значению – быть >1. Ещё один способ для чисел быть моноидами состоит в том, чтобы в качестве бинарной функции выступал оператор >+, а в качестве единичного значения – значение >0:

>ghci> 0 + 4

>4

>ghci> 5 + 0

>5

>ghci> (1 + 3) + 5

>9

>ghci> 1 + (3 + 5)

>9

Законы моноидов выполняются, потому что если вы прибавите 0 к любому числу, результатом будет то же самое число. Сложение также ассоциативно, поэтому здесь у нас нет никаких проблем.

Итак, в нашем распоряжении два одинаково правомерных способа для чисел быть моноидами. Какой же способ выбрать?.. Ладно, мы не обязаны выбирать! Вспомните, что когда имеется несколько способов определения для какого-то типа экземпляра одного и того же класса типов, мы можем обернуть этот тип в декларацию >newtype, а затем сделать для нового типа экземпляр класса типов по-другому. Можно совместить несовместимое.

Модуль >Data.Monoid экспортирует для этого два типа: >Product и >Sum.

>Product определён вот так:

>newtype Product a = Product { getProduct :: a }

>   deriving (Eq, Ord, Read, Show, Bounded)

Это всего лишь обёртка >newtype с одним параметром типа наряду с некоторыми порождёнными экземплярами. Его экземпляр для класса >Monoid выглядит примерно так:

>instance Num a => Monoid (Product a) where

>   mempty = Product 1

>   Product x `mappend` Product y = Product (x * y)

Значение >mempty – это просто 1, обёрнутая в конструктор >Product. Функция >mappend производит сопоставление конструктора >Product с образцом, перемножает два числа, а затем оборачивает результирующее число. Как вы можете видеть, имеется ограничение класса >Num a. Это значит, что