Как легко использовать моноидные и комбинировать значения с пользовательской операцией?
То, что я пытаюсь сделать, тривиально определить вручную, в основном
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'
Это не очень важно для определять это локально, когда это необходимо, но, тем не менее, он является основным и общим, кажется, что должна быть стандартная реализация, но я могу Кажется, он не нашел.
Возможно, я просто что-то пропустил. То, что я хочу, кажется совершенно не связанным с поведением монады, поэтому я считаю, что ничего не найду в ящиках Monad/Arrow; но он точно напоминает экземпляр Monoid
Prelude Data.Monoid > Только "a" < > Nothing
Просто "а"
Prelude Data.Monoid > Просто "a" < > Только "b"
Просто "ab"
...
... который, однако, требует, чтобы a
был само моноидом, т.е. что он в основном имеет a->a->a
"встроенный". Экземпляр MonadPlus
также ведет себя так же, как я хочу, но он просто выбрасывает одно из значений, а не позволяет мне комбинировать функцию
Prelude Data.Monoid Control.Monad > Только 4 `mplus` Nothing
Всего 4 | Prelude Data.Monoid Control.Monad > Nothing `mplus` Just 4
Всего 4 | Prelude Data.Monoid Control.Monad > Просто 4 `mplus` Всего 5
Всего 4
Каким будет каноническое решение? Локальное сопоставление шаблонов? Что-то с комбинаторами из, например, Data.Maybe
? Определение пользовательского моноида для объединения?
Ответы
Ответ 1
Вы правы на деньги, заметив, что f
похож на операцию Monoid
в базовом типе a
. Более конкретно, что происходит здесь, вы поднимаете Semigroup
в Monoid
путем смещения нуля (mempty
), Nothing
.
Это именно то, что вы видите в Haddocks для Maybe
Monoid
на самом деле.
Поднимите полугруппу в возможно формирование моноида в соответствии с http://en.wikipedia.org/wiki/Monoid: "Любая полугруппа S может быть превращена в моноид просто путем присоединения элемент e не в S и определяющий ee = e и es = s = s * e для всех s ∈ S." Поскольку нет класса "Semigroup", который предоставляет только mappend, мы вместо этого используем Monoid.
Или, если вам нравится пакет semigroups
, тогда Option
, который имеет именно такое поведение, соответствующим образом обобщенное для использования базового Semigroup
.
Таким образом, самым ясным способом является определение экземпляра Monoid
или Semigroup
для базового типа a
. Это чистый способ связать некоторый сумматор f
с этим типом.
Что делать, если вы не контролируете этот тип, не хотите экземпляры-сироты и думаете, что оболочка newtype
уродлива? Обычно вам не повезло, но это одно место, где используется общая черная магия, фактически GHC-only reflection
пакет приходит удобно. Тщательные объяснения существуют в самой статье, но Ausin Seipp FP Complete Tutorial включает в себя некоторый пример кода, позволяющий вам "вводить" произвольные полугрупповые продукты в типы без (как много) шума определения типа... ценой гораздо более страшных сигнатур.
Это, вероятно, значительно больше накладных расходов, чем его стоимость.
Ответ 2
Вы всегда можете использовать
f <$> m <*> n <|> m <|> n
Но это, к сожалению, не имеет канонической реализации где угодно.
Вы можете использовать reflection
, чтобы получить (a -> a -> a)
"испеченный в" как Semigroup
для использования с Option
, который предоставляется semigroups
как улучшенная версия Maybe
, которая имеет ' right "для Monoid
в терминах Semigroup
. Однако это слишком тяжело для этой проблемы. =)
Возможно, это должно быть просто добавлено как комбинатор к Data.Maybe
.
Ответ 3
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