Как легко использовать моноид 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
последовательность