我记得这是非常基本的,甚至可以通过像(\a b -> (fst a + fst b, snd a + snd b) ) (1,2) (3,4)
之类的lambda来完成模式匹配。但是,我认为应该有Haskell标准库提供的方法来做这样的事情。 mappend
类型的(a,b)
定义为Monoids看起来非常相似。但是做以下事情并不奏效:
(1,2) `mappend` (3,4)
任何添加两个2元组的Haskell方式?
答案 0 :(得分:6)
数字是幺半群,这不是问题。问题是有两种不同的,同样好的方式,它们是幺半群 - 加法只有一种,另一种是乘法。因此标准库决定不给出一个monoid实例,我认为这是一个很好的决定。
Haskell允许 newtype wrappers 中的打包类型选择实例而不影响基础类型。两个不同的monoid实例可用于基础:
Prelude Data.Monoid> case (Sum 1, Sum 2)<>(Sum 3, Sum 4) of (Sum a, Sum b) -> (a,b)
(4,6)
有点尴尬。有几种方法可以让你更简洁(我不会推荐其中任何一种;请参阅底部以获得更好的解决方案)。首先,正如Jon在注释中所说的那样,如果元组只包含像你的例子那样的普通数字文字,那么就没有必要在Sum
构造函数中显式地包装它们,因为有一个实例Num a => Num (Sum a)
和一个数字文字是多态的,即以下内容:
Prelude Data.Monoid> case (1,2) <> (3,4) of (Sum x, Sum y) -> (x,y)
(4,6)
但是,如果元组元素的类型已经修复......
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)
在这里你需要明确包装应该发生的地方。您仍然可以使用type-safe coercions来简要介绍它,它允许您将整个容器的所有元素(元组,列表,映射,数组......)包装在一个新类型中,例如Sum
in一个去(并在 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)
更简洁,更少依赖本地签名,可能是使用Newtype
class的方法:
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)
...但是,由于我现在很惊讶地发现,newtype
库没有附带为此所必需的元组实例。您可以自己定义:
> :set -XFunctionalDependencies -XUndecidableInstances
> instance (Newtype a α, Newtype b β) => Newtype (a,b) (α,β) where {pack=pack***pack; unpack=unpack***unpack}
如果您真的只需要添加剂monoid,我建议您使用专用的类添加:AdditiveGroup
Prelude Data.AdditiveGroup> (1,2)^+^(3,4)
(4,6)
答案 1 :(得分:3)
如果您允许我忽略Monoid
在您的问题中的作用,可以通过以下几种方式来表达Biapplicative
from bifunctors,这是对Applicative
的一种非常直接的概括。到Bifunctor
s:
GHCi> import Data.Biapplicative
GHCi> :t bimap
bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d
GHCi> :t (<<*>>)
(<<*>>) :: Biapplicative p => p (a -> b) (c -> d) -> p a c -> p b d
GHCi> bimap (+) (+) (1,2) <<*>> (3,4)
(4,6)
GHCi> :t biliftA2
biliftA2
:: Biapplicative w =>
(a -> b -> c) -> (d -> e -> f) -> w a d -> w b e -> w c f
GHCi> biliftA2 (+) (+) (1,2) (3,4)
(4,6)
答案 2 :(得分:1)
这也可以用一些简单而愚蠢但奇特的方式完成(虽然这种方法需要将 Haskell 转换为 Lisp )。
{-# LANGUAGE PostfixOperators #-}
import Data.Bifunctor (bimap)
import Data.Semigroup (Sum (..), (<>))
(+?) :: (a, b) -> (Sum a, Sum b)
(+?) = bimap Sum Sum
(+!) :: (Sum a, Sum b) -> (a, b)
(+!) = bimap getSum getSum
然后在ghci
:
ghci> (( ((1,2) +?) <> ((3,4) +?)) +!)
(4,6)