11

我想要做的是手动定义微不足道,基本上

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 的行为完全无关,所以我想我不会在 Monad/Arrow 抽屉里找到任何东西;但它确实类似于Monoid实例

Prelude Data.Monoid> Just "a" <> Nothing
Just "a"
Prelude Data.Monoid> Just "a" <> Just "b"
Just "ab"
...

...但是a它本身需要是一个幺半群,即它基本上具有a->a->a“内置”。该MonadPlus实例的行为也很像我想要的,但它只是丢弃了其中一个值,而不是允许我提供组合函数

Prelude Data.Monoid Control.Monad> Just 4 `mplus` Nothing
Just 4
Prelude Data.Monoid Control.Monad> Nothing `mplus` Just
4
Prelude Data.Monoid Control.Monad> Just 4 `mplus` Just 5
Just 4

什么是规范的解决方案?本地模式匹配?来自例如的组合器的东西Data.Maybe?定义一个自定义幺半群来进行组合?

4

3 回答 3

12

您可以随时使用

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

但遗憾的是,这在任何地方都没有规范的实现。

您可以使用reflectionto 将(a -> a -> a)“烘焙”作为与Semigroup一起使用的Option,它是semigroups作为改进版本提供的,它Maybe具有 的“正确”实例MonoidSemigroup不过,这对于这个问题来说太重了。=)

也许这应该作为组合器添加到Data.Maybe.

于 2013-10-31T12:47:56.867 回答
11

当您注意到这f就像Monoid对基础a类型的操作时,您是对的。更具体地说,这里发生的事情是您通过将a 与零 ( )相邻来提升Semigroupa , 。MonoidmemptyNothing

实际上,这正是您在黑线鳕中看到的Maybe Monoid

根据http://en.wikipedia.org/wiki/Monoid将一个半群提升到可能形成一个 Monoid :“任何半群 S 都可以简单地通过邻接不在 S 中的元素 e 并定义 e e = e 和对于所有 s ∈ S,e s = s = s*e。" 由于没有仅提供 mappend 的“Semigroup”类型类,我们使用 Monoid 代替。

或者,如果你喜欢这个semigroups包,那么它就Option具有这种行为,适当地概括为使用底层Semigroup


因此,这表明最清晰的方法是在基础类型上定义 aMonoid或instance 。这是一种将某些组合器与该类型相关联的干净方法。Semigroupaf

如果您不控制该类型,不想要孤立实例,并且认为newtype包装器丑陋怎么办?通常你会不走运,但这是一个使用完全黑魔法的地方,有效地仅 GHCreflection包派上用场。论文本身有详尽的解释,但Ausin Seipp 的 FP Complete Tutorial包含一些示例代码,允许您将任意半群产品“注入”到没有(尽可能多的)类型定义噪音的类型中......以更可怕的签名为代价。 

然而,这可能比它的价值要多得多。

于 2013-10-31T12:44:45.020 回答
2
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

于 2013-10-31T13:36:43.880 回答