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

или >Just; мы можем просто продолжить обрабатывать их как обычные моноиды.

Но что если тип содержимого типа >Maybe не имеет экземпляра класса >Monoid? Обратите внимание: в предыдущем объявлении экземпляра единственный случай, когда мы должны полагаться на то, что содержимые являются моноидами, – это когда оба параметра функции >mappend обёрнуты в конструктор >Just. Когда мы не знаем, являются ли содержимые моноидами, мы не можем использовать функцию >mappend между ними; так что же нам делать? Ну, единственное, что мы можем сделать, – это отвергнуть второе значение и оставить первое. Для этой цели существует тип >First>a. Вот его определение:

>newtype First a = First { getFirst :: Maybe a }

>   deriving (Eq, Ord, Read, Show)

Мы берём тип >Maybe a и оборачиваем его с помощью декларации >newtype. Экземпляр класса >Monoid в данном случае выглядит следующим образом:

>instance Monoid (Firsta) where

>   mempty = First Nothing

>   First (Just x) `mappend` _ = First (Just x)

>   First Nothing `mappend` x = x

Значение >mempty – это просто >Nothing, обёрнутое с помощью конструктора >First. Если первый параметр функции >mappend является значением >Just, мы игнорируем второй. Если первый параметр – >Nothing, тогда мы возвращаем второй параметр в качестве результата независимо от того, является ли он >Just или >Nothing:

>ghci> getFirst $ First (Just 'a') `mappend` First (Just 'b')

>Just 'a'

>ghci> getFirst $ First Nothing `mappend` First (Just 'b')

>Just 'b'

>ghci> getFirst $ First (Just 'a') `mappend` First Nothing

>Just 'a'

Тип >First полезен, когда у нас есть множество значений типа >Maybe и мы хотим знать, является ли какое-либо из них значением >Just. Для этого годится функция >mconcat:

>ghci> getFirst . mconcat . map First $ [Nothing, Just 9, Just 10]

>Just 9

Если нам нужен моноид на значениях >Maybe a – такой, чтобы оставался второй параметр, когда оба параметра функции >mappend являются значениями >Just, то модуль >Data.Monoid предоставляет тип >Last a, который работает, как и тип >First a, но при объединении с помощью функции >mappend и использовании функции >mconcat сохраняется последнее значение, не являющееся >Nothing:

>ghci> getLast . mconcat . map Last $ [Nothing, Just 9, Just 10]

>Just 10

>ghci> getLast $ Last (Just "один") `mappend` Last (Just "два")

>Just "two"

Свёртка на моноидах

Один из интересных способов ввести моноиды в работу заключается в том, чтобы они помогали нам определять свёртки над различными структурами данных. До сих пор мы производили свёртки только над списками, но списки – не единственная структура данных, которую можно свернуть. Мы можем определять свёртки почти над любой структурой данных. Особенно хорошо поддаются свёртке деревья.