Мы создали вспомогательную функцию, которая принимает строку и сообщает нам, сколько она содержит гласных звуков, сначала отфильтровывая в ней только буквы, находящиеся в строке >"аеёиоуыэюя"
, а затем применяя функцию >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})
Это полезно, когда мы имеем дело с моноидами как с результатами вычислений, которые могли окончиться неуспешно. Из-за наличия этого экземпляра нам не нужно проверять, окончились ли вычисления неуспешно, определяя, вернули они значение