Мы создали вспомогательную функцию, которая принимает строку и сообщает нам, сколько она содержит гласных звуков, сначала отфильтровывая в ней только буквы, находящиеся в строке >"аеёиоуыэюя", а затем применяя функцию >length.
>ghci> lengthCompare "ямб" "абыр"
>LT
>ghci> lengthCompare "ямб" "абы"
>LT
>ghci> lengthCompare "ямб" "абр"
>GT
В первом примере длины оказались различными, поэтому вернулось >LT, так как длина слова >"ямб" меньше длины слова >"абыр". Во втором примере длины равны, но вторая строка содержит больше гласных звуков, поэтому опять возвращается >LT. В третьем примере они обе имеют одинаковую длину и одинаковое количество гласных звуков, поэтому сравниваются по алфавиту, и слово >"ямб" выигрывает.
Моноид для типа >Ordering очень полезен, поскольку позволяет нам без труда сравнивать сущности по большому количеству разных критериев и помещать сами эти критерии по порядку, начиная с наиболее важных и заканчивая наименее важными.
Рассмотрим несколько способов, которыми для типа >Maybe a могут быть определены экземпляры класса >Monoid, и обсудим, чем эти экземпляры полезны.
Один из способов состоит в том, чтобы обрабатывать тип >Maybe a как моноид, только если его параметр типа >a тоже является моноидом, а потом реализовать функцию >mappend так, чтобы она использовала операцию >mappend для значений, обёрнутых в конструктор >Just. Мы используем значение >Nothing как единичное, и поэтому если одно из двух значений, которые мы объединяем с помощью функции >mappend, равно >Nothing, мы оставляем другое значение. Вот объявление экземпляра:
>instance Monoid a => Monoid (Maybe a) where
> mempty = Nothing
> Nothing `mappend` m = m
> m `mappend` Nothing = m
> Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
Обратите внимание на ограничение класса. Оно говорит, что тип >Maybe является моноидом, только если для типа >a определён экземпляр класса >Monoid. Если мы объединяем нечто со значением >Nothing, используя функцию >mappend, результатом является это нечто. Если мы объединяем два значения >Just с помощью функции >mappend, то содержимое значений >Just объединяется с помощью этой функции, а затем оборачивается обратно в конструктор >Just. Мы можем делать это, поскольку ограничение класса гарантирует, что тип значения, которое находится внутри >Just, имеет экземпляр класса >Monoid.
>ghci> Nothing `mappend` Just "андрей"
>Just "андрей"
>ghci> Just LT `mappend` Nothing
>Just LT
>ghci> Just (Sum 3) `mappend` Just (Sum 4)
>Just (Sum {getSum = 7})
Это полезно, когда мы имеем дело с моноидами как с результатами вычислений, которые могли окончиться неуспешно. Из-за наличия этого экземпляра нам не нужно проверять, окончились ли вычисления неуспешно, определяя, вернули они значение