将(a1,b1)和(a2,b2)映射到(a1 + a2,b1 + b2)

hyi*_*tiz 2 monads haskell applicative monoids

我记得这是非常基本的,可以简单地通过模式匹配来完成,即使是像lambda一样(\a b -> (fst a + fst b, snd a + snd b) ) (1,2) (3,4).但是,我认为应该有Haskell标准库提供的方法来做这样的事情.的定义mappend(a,b)类型幺看起来颇为相似.但是执行以下操作不起作用:

(1,2) `mappend` (3,4)
Run Code Online (Sandbox Code Playgroud)

任何Haskell添加两个2元组的方式?

lef*_*out 6

数字是幺半群,这不是问题.问题在于它们有两种不同的,同样好的方式,它们是幺半群 - 加法只有一种,另一种是乘法.因此标准库决定不给出一个monoid实例,我认为这是一个很好的决定.

Haskell允许在newtype包装器中打包类型以选择实例而不影响基础类型.两个不同的monoid实例以base为基础:

Prelude Data.Monoid> case (Sum 1, Sum 2)<>(Sum 3, Sum 4) of (Sum a, Sum b) -> (a,b)
(4,6)
Run Code Online (Sandbox Code Playgroud)

有点尴尬.有几种方法可以让你更简洁(我不推荐任何一种方法;请参阅底部以获得更好的解决方案).首先,正如Jon在注释中所说的那样,如果元组只包含像你的例子那样的普通数字文字,则不需要在Sum构造函数中显式地包装它们,因为有一个实例Num a => Num (Sum a),数字文字是多态的,即以下内容将:

Prelude Data.Monoid> case (1,2) <> (3,4) of (Sum x, Sum y) -> (x,y)
(4,6)
Run Code Online (Sandbox Code Playgroud)

但是,如果元组元素的类型已经修复了......

Prelude Data.Monoid> let [a,b,c,d] = [1,2,3,4] :: [Int]
Prelude Data.Monoid> case (a,b) <> (c,d) of (Sum x, Sum y) -> (x,y) :: (Int,Int) 
<interactive>:6:25:
    Couldn't match expected type ‘Int’ with actual type ‘Sum Int’
    In the pattern: Sum x
    In the pattern: (Sum x, Sum y)
    In a case alternative: (Sum x, Sum y) -> (x, y) :: (Int, Int)
Run Code Online (Sandbox Code Playgroud)

在这里你需要明确包装应该发生的地方.您仍然可以通过使用类型安全强制来简要介绍它,它允许您将整个容器(元组,列表,映射,数组......)的所有元素包装在一个新类型中,例如Sum一次性(和O中)(1)时间和空间,这也可以是一个很大的奖励).

Prelude Data.Monoid Data.Coerce> case coerce (a,b) <> coerce (c,d) of (Sum x, Sum y) -> (x,y) :: (Int,Int)
(4,6)
Run Code Online (Sandbox Code Playgroud)

即使更简洁,也更少依赖本地签名,可能是使用Newtype该类的方法:

Prelude Data.Monoid> :m +Control.Newtype
Prelude Data.Monoid Control.Newtype> :m +Control.Arrow
Prelude Data.Monoid Control.Newtype Control.Arrow> :simpleprompt 
> ala (Sum***Sum) foldMap [(1,2), (3,4)]
(4,6)
Run Code Online (Sandbox Code Playgroud)

...但是,由于我现在很惊讶地发现,newtype库并没有附带为此所必需的元组实例.您可以自己定义:

> :set -XFunctionalDependencies -XUndecidableInstances
> instance (Newtype a ?, Newtype b ?) => Newtype (a,b) (?,?) where {pack=pack***pack; unpack=unpack***unpack}
Run Code Online (Sandbox Code Playgroud)

如果你真的只需要添加剂monoid,我建议你改用专用的:AdditiveGroup

Prelude Data.AdditiveGroup> (1,2)^+^(3,4)
(4,6)
Run Code Online (Sandbox Code Playgroud)