如何使用may monoid并将值与自定义操作相结合?

时间:2013-10-31 12:25:17

标签: haskell monoids

我想做的事情是手工定义,基本上是

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'

这对define this locally when needed来说并不是什么大不了的事,但仍然是笨拙的,并且如此基本和普遍,似乎应该有一个标准的实现,但我似乎找不到一个。

也许我只是忽略了一些东西。我想要的似乎与monad的行为完全无关,所以我认为我在Monad / Arrow抽屉里找不到任何东西;但它确实类似于Monoid实例

  

Prelude Data.Monoid>只是“一个”<>没有   
只是“一个”    
Prelude Data.Monoid>只是“一个”<>只是“b”     
只是“ab”     
...

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

  

Prelude Data.Monoid Control.Monad>只是4` mplus`什么都没有   只是4
  Prelude Data.Monoid Control.Monad>没什么`mplus`只是4
  只是4
  Prelude Data.Monoid Control.Monad>只需4` mplus`只需5   仅4岁

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

3 个答案:

答案 0 :(得分:12)

您可以随时使用

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

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

您可以使用reflection将“{1}}”作为(a -> a -> a)加入Semigroup,与Option一起使用semigroups作为Maybe的改进版本,Monoid具有Semigroup的“正确”实例。尽管如此,这对于这个问题来说太过沉重。 =)

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

答案 1 :(得分:11)

如果您发现f与基础Monoid类型的a操作类似,那么您就是对的。更具体地说,这里发生的事情是,您将Semigroup提升为Monoid,与零(mempty),Nothing相邻。

这正是你在Haddocks中实际看到的Maybe Monoid

  

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

或者,如果你喜欢semigroups软件包,那么Option就会出现这种行为,适当地推广使用基础Semigroup


因此,这表明最明确的方法是在基础类型Monoid上定义Semigroupa实例。将一些组合器f与该类型相关联是一种干净的方法。

如果您不控制该类型,不想要孤立实例,并认为newtype包装器难看,该怎么办?通常情况下你运气不好,但这是一个使用全黑魔法的地方,有效的GHC专用reflection包就派上用场了。存在彻底的解释in the paper itself但是Ausin Seipp's FP Complete Tutorial包括一些示例代码,允许您将任意半群产品“注入”到没有(尽可能多)类型定义噪声的类型中......代价是更加可怕的签名。

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

答案 2 :(得分: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