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

является экземпляром >Monoid для всех значений типа >a, для которых уже имеется экземпляр класса >Num. Для того чтобы использовать тип >Product a в качестве моноида, мы должны произвести некоторое оборачивание и разворачивание >newtype:

>ghci> getProduct $ Product 3 `mappend` Product 9

>27

>ghci> getProduct $ Product 3 `mappend` mempty

>3

>ghci> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2

>24

>ghci> getProduct . mconcat . map Product $ [3,4,2]

>24

Тип >Sum определён в том же духе, что и тип >Product, и экземпляр тоже похож. Мы используем его точно так же:

>ghci> getSum $ Sum 2 `mappend` Sum 9

>11

>ghci> getSum $ mempty `mappend` Sum 3

>3

>ghci> getSum . mconcat . map Sum $ [1,2,3]

>6

Типы Any и All

Ещё одним типом, который может действовать как моноид двумя разными, но одинаково допустимыми способами, является >Bool. Первый способ состоит в том, чтобы заставить функцию >||, которая представляет собой логическое ИЛИ, действовать как бинарная функция, используя >False в качестве единичного значения. Если при использовании логического ИЛИ какой-либо из параметров равен >True, функция возвращает >True; в противном случае она возвращает >False. Поэтому если мы используем >False в качестве единичного значения, операция ИЛИ вернёт >False при использовании с >False – и >True при использовании с >True. Конструктор >newtype Any аналогичным образом имеет экземпляр класса >Monoid. Он определён вот так:

>newtype Any = Any { getAny :: Bool }

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

А его экземпляр выглядит так:

>instance Monoid Any where

>   mempty = Any False

>   Any x `mappend` Any y = Any (x || y)

Он называется >Any, потому что >x `mappend` y будет равно >True, если любое из этих двух значений равно >True. Даже когда три или более значений >Bool, обёрнутых в >Any, объединяются с помощью функции >mappend, результат будет содержать >True, если любое из них равно >True.

>ghci> getAny $ Any True `mappend` Any False

>True

>ghci> getAny $ mempty `mappend` Any True

>True

>ghci> getAny . mconcat . map Any $ [False, False, False, True]

>True

>ghci> getAny $ mempty `mappend` mempty

>False

Другой возможный вариант экземпляра класса >Monoid для типа >Bool – всё как бы наоборот: заставить оператор >&& быть бинарной функцией, а затем сделать значение >True единичным значением. Логическое И вернёт >True, только если оба его параметра равны >True.

Это объявление >newtype:

>newtype All = All { getAll :: Bool }

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

А это экземпляр:

>instance Monoid All where

>   mempty = All True

>   All x `mappend` All y = All (x && y)

Когда мы объединяем значения типа