Как легко использовать моноид maybe и комбинировать значения с пользовательской операцией?

то, что я пытаюсь сделать, тривиально определить вручную, в основном

maybeCombine :: (a->a->a) -> Maybe a -> Maybe a -> Maybe a
maybeCombine _ Nothing Nothing = Nothing
maybeCombine _ (Just a) Nothing = Just a
maybeCombine _ Nothing (Just a) = Just a
maybeCombine f (Just a) (Just a') = Just $ f a a'

это не имеет большого значения для определите это локально, когда это необходимо, но все еще громоздкий и настолько простой и общий, что, кажется, должна быть стандартная реализация, но я не могу ее найти.

возможно, я просто что-то упускаю. То, что я хочу, кажется совершенно не связанным с поведением монады, поэтому я думаю, что ничего не найду в ящиках монады / стрелки; но это, конечно, напоминает Monoid экземпляр

Данные Прелюдия.Моноид> просто " а " ничего
просто " a"
Данные Прелюдия.Моноид> просто " a " просто " b"
просто " ab"
...

...что, однако, требует a чтобы быть моноидом, т. е. что он в основном имеет a->a->a "встроенный". The MonadPlus экземпляр также ведет себя так, как я хочу, но он просто выбрасывает одно из значений, а не позволяет мне поставлять комбинированная функция

Данные Прелюдия.Моноидный Контроль.Монада> просто 4 'mplus' ничего
Просто 4
Данные Прелюдии.Моноидный Контроль.Монада > ничего "mplus" просто 4
Просто 4
Данные Прелюдии.Моноидный Контроль.Монада> всего 4 'mplus' всего 5
Просто 4

каким будет каноническое решение? локальные сопоставления с образцом? Что-то с комбинаторами, например,Data.Maybe? Определение пользовательского моноида для выполнения сочетая?

3 ответов


вы правы на деньги, когда вы заметите, что f Как Monoid операция на базовом a тип. Более конкретно, что здесь происходит, вы поднимаете Semigroup на Monoid путем присоединения нуля (mempty),Nothing.

это именно то, что вы видите в пикши для Maybe Monoid на самом деле.

лифт полугруппа в формировании моноидом по http://en.wikipedia.org/wiki/Monoid: "любая полугруппа S может быть превращена в моноид просто путем присоединения элемента e не в S и определения ee = e и es = s = s*e для всех S ∈ S. "поскольку нет" Полугруппового " типа, предоставляющего только mappend, мы используем моноид вместо этого.

или, если вам нравится semigroups, то есть Option который имеет именно это поведение, соответствующим образом обобщенное для использования в основе .


таким образом, это предполагает, что самый ясный способ-определить либо Monoid или Semigroup экземпляр базового типа a. Это чистый способ связать некоторые combiner f с этим типом.

что делать, если вы не контролируете этот тип, не хотите сиротских экземпляров и думаете newtype фантик-это некрасиво? Обычно вам не повезло, но это одно место, где использование полной черной магии, эффективно GHC-только reflection пакет пригодится. Существуют подробные объяснения в самом документе но Ausin Seipp в FP полный учебник включает в себя некоторый пример кода, позволяющий "вводить" произвольные полугрупповые продукты в типы без (столько же) шума определения типа... ценой гораздо более страшных подписей. 

это, вероятно, значительно больше накладных расходов, чем его стоимость, однако.


вы всегда можете использовать

f <$> m <*> n <|> m <|> n

но это, к сожалению, не имеет канонической реализации нигде.

можно использовать reflection получить (a -> a -> a) "запеченный в" как Semigroup для использования с Option, предусмотренного semigroups как улучшенная версия Maybe это имеет "правильный" экземпляр для Monoid С точки зрения Semigroup. Однако это слишком тяжело для этой проблемы. =)

возможно, это следует просто добавить в качестве комбинатора Data.Maybe.


import Data.Monoid
maybeCombine :: (a->a->a) -> Maybe a -> Maybe a -> Maybe a
maybeCombine f mx my = let combine = mx >>= (\x -> my >>= (\y -> Just (f x y)))
                       in getFirst $ First combine `mappend` First mx `mappend` First` my

в GHCi, это дает мне

*Main> maybeCombine (+) Nothing Nothing
Nothing
*Main> maybeCombine (+) (Just 3) Nothing
Just 3
*Main> maybeCombine (+) (Just 3) (Just 5)
Just 8

работает с getLast если поставить Last combine В конце mappend последовательность