如何使用可能的monoid并将值与自定义操作相结合,轻松? [英] How to use the maybe monoid and combine values with a custom operation, easily?

查看:107
本文介绍了如何使用可能的monoid并将值与自定义操作相结合,轻松?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要做的事情很简单,只需手工定义,基本上就是

  maybeCombine ::(a-> a-> a) - >也许是 - >也许是 - >也许
maybeCombine _ Nothing Nothing = Nothing
maybeCombine _(Just a)Nothing =只是一个
maybeCombine _ Nothing(只是一个)=只是一个
maybeCombine f(只是一个) (Just a')= Just $ fa a'

在需要时在本地定义此项,但仍然很烦琐,而且基础性和通用性都很高似乎应该有一个标准的实现,但我似乎无法找到一个。



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


Prelude Data.Monoid>只是a <> Nothing

只是a

Prelude Data.Monoid>只是a<>只是b

只是ab

...

...但需要 a 本身就是一个monoid,即它基本上具有 a-> a-> a built in。 MonadPlus 实例的行为也与我想要的非常相似,但它只是丢弃其中一个值,而不是允许我提供组合函数


Prelude Data.Monoid Control.Monad>只有4`mplus`没有

只有4

前奏Data.Monoid Control.Monad > Nothing`mplus`只有4

只有4

序曲Data.Monoid Control.Monad>只有4`mplus`只有5

只有4


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

解决方案

当你注意到<$ c $对基础 a 类型,c> f 类似于 Monoid 操作。更具体地说,这里发生的事情是通过将 Semigroup 连接到一个零(<$ c c>)来解决 Monoid $ c> mempty ), Nothing



这就是您看到的实际上 Maybe Monoid 的Haddocks。


根据 http://en.wikipedia.org将一个半群提升为Maybe构成一个Monoid / wiki / Monoid :任何半群S都可以简单地通过连接一个不在S中的元素e并且定义e = e = e和e = s = s = s * e对于所有s∈S。因为没有提供mappend的Semigroup类型类型,所以我们使用Monoid。

或者,如果您喜欢 semigroups 包,那么就有 选项 它恰好具有这种行为,适当地推广到使用基础 Semigroup 代替。






因此,建议最明确的方法是在基础类型上定义 Monoid Semigroup code> A 。这是一种将某些组合器 f 与该类型相关联的干净方式。



如果您不控制该类型,不想孤儿实例,并认为 newtype 包装很丑陋?通常你会失败,但这是一个使用全黑魔法的地方,实际上只有GHC reflection 软件包派上用场。 在论文本身中有详细的解释,但 Ausin Seipp的FP完整教程包含了一些示例代码,允许您将任意半群产品注入到没有尽可能多的)类型定义噪声......这是以很多可怕的签名为代价的。 

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


What I'm trying to do is trivial to define by hand, basically

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'

It's not a big deal to define this locally when needed, but still cumbersone and being so basic and general it seems there should be a standard implementation, yet I can't seem to find one.

Perhaps I'm just overlooking something. What I want seems quite unrelated on the behaviour of the maybe monad, so I reckon I won't find anything in the Monad/Arrow drawers; but it sure resembles the Monoid instance

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

...which however requires a to be a monoid itself, i.e. that it basically has the a->a->a "built in". The MonadPlus instance also behaves much like I want, but it simply throws away one of the values rather than allowing me to supply a combination function

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

What would be the canonical solution? Local pattern matching? Something with combinators from e.g. Data.Maybe? Defining a custom monoid to do the combining?

解决方案

You're right on the money when you notice that the f is like a Monoid operation on the underlying a type. More specifically what's going on here is you're lifting a Semigroup into a Monoid by adjoining a zero (mempty), Nothing.

This is exactly what you see in the Haddocks for the Maybe Monoid actually.

Lift a semigroup into Maybe forming a Monoid according to http://en.wikipedia.org/wiki/Monoid: "Any semigroup S may be turned into a monoid simply by adjoining an element e not in S and defining ee = e and es = s = s*e for all s ∈ S." Since there is no "Semigroup" typeclass providing just mappend, we use Monoid instead.

Or, if you like the semigroups package, then there's Option which has exactly this behavior, suitably generalized to use an underlying Semigroup instead.


So that suggests the clearest way is to define either a Monoid or Semigroup instance on the underlying type a. It's a clean way to associate some combiner f with that type.

What if you don't control that type, don't want orphan instances, and think a newtype wrapper is ugly? Normally you'd be out of luck, but this is one place where using the total black magic, effectively GHC-only reflection package comes in handy. Thorough explanations exist in the paper itself but Ausin Seipp's FP Complete Tutorial includes some example code to allow you to "inject" arbitrary semigroup products into types without (as much) type definition noise... at the cost of a lot scarier signatures. 

That's probably significantly more overhead than its worth, however.

这篇关于如何使用可能的monoid并将值与自定义操作相结合,轻松?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆