или
>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"