», мы знаем, в каком порядке должны следовать эти слова. Чтобы лучше понять, как
>EQ
является единичным значением, обратите внимание, что если бы мы втиснули одну и ту же букву в одну и ту же позицию в обоих словах, их расположение друг относительно друга в алфавитном порядке осталось бы неизменным; к примеру, слово «
oix» будет по-прежнему идти следом за «
oin».
Важно, что в экземпляре класса >Monoid
для типа >Ordering
выражение >x `mappend` y
не равно выражению >y `mappend` x
. Поскольку первый параметр сохраняется, если он не равен >EQ
, >LT `mappend` GT
в результате вернёт >LT
, тогда как >GT `mappend` LT
в результате вернёт >GT
:
>ghci> LT `mappend` GT
>LT
>ghci> GT `mappend` LT
>GT
>ghci> mempty `mappend` LT
>LT
>ghci> mempty `mappend` GT
>GT
Хорошо, так чем же этот моноид полезен? Предположим, мы пишем функцию, которая принимает две строки, сравнивает их длину и возвращает значение типа >Ordering
. Но если строки имеют одинаковую длину, то вместо того, чтобы сразу вернуть значение >EQ
, мы хотим установить их расположение в алфавитном порядке.
Вот один из способов это записать:
>lengthCompare :: String –> String –> Ordering
>lengthCompare x y = let a = length x `compare` length y
> b = x `compare` y
> in if a == EQ then b else a
Результат сравнения длин мы присваиваем образцу >a
, результат сравнения по алфавиту – образцу >b
; затем, если оказывается, что длины равны, возвращаем их порядок по алфавиту.
Но, имея представление о том, что тип >Ordering
является моноидом, мы можем переписать эту функцию в более простом виде:
>import Data.Monoid
>lengthCompare :: String –> String –> Ordering
>lengthCompare x y = (length x `compare` length y) `mappend`(x `compare` y)
Давайте это опробуем:
>ghci> lengthCompare "ямб" "хорей"
>LT
>ghci> lengthCompare "ямб" "хор"
>GT
Вспомните, что когда мы используем функцию >mappend
, сохраняется её левый параметр, если он не равен значению >EQ
; если он равен >EQ
, сохраняется правый. Вот почему мы поместили сравнение, которое мы считаем первым, более важным критерием, в качестве первого параметра. Теперь предположим, что мы хотим расширить эту функцию, чтобы она также сравнивала количество гласных звуков, и установить это вторым по важности критерием для сравнения. Мы изменяем её вот так:
>import Data.Monoid
>lengthCompare :: String –> String –> Ordering
>lengthCompare x y = (length x `compare` length y) `mappend`
> (vowels x `compare` vowels y) `mappend`
> (x `compare` y)
> where vowels = length . filter (`elem` "аеёиоуыэюя")