Типы 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)

Когда мы объединяем значения типа All с помощью функции mappend, результатом будет True только в случае, если все значения, использованные в функции mappend, равны True:

ghci> getAll $ mempty `mappend` All True

True

ghci> getAll $ mempty `mappend` All False

False

ghci> getAll . mconcat . map All $ [True, True, True]

True

ghci> getAll . mconcat . map All $ [True, True, False]

False

Так же, как при использовании умножения и сложения, мы обычно явно указываем бинарные функции вместо оборачивания их в значения newtype и последующего использования функций mappend и mempty. Функция mconcat кажется полезной для типов Any и All, но обычно проще использовать функции or и and. Функция or принимает списки значений типа Bool и возвращает True, если какое-либо из них равно True. Функция and принимает те же значения и возвращает значение True, если все из них равны True.