Haskell 将(a1,b1)和(a2,b2)映射为(a1和a2,b1和b2)
我记得这是非常基本的,甚至可以通过类似lambda的Haskell 将(a1,b1)和(a2,b2)映射为(a1和a2,b1和b2),haskell,monads,applicative,monoids,Haskell,Monads,Applicative,Monoids,我记得这是非常基本的,甚至可以通过类似lambda的(\a b->(fst a+fst b,snd a+snd b))(1,2)(3,4)简单地进行模式匹配。然而,我认为Haskell标准库应该提供一些方法来完成这样的事情。(a,b)类型的mappend作为幺半群的定义看起来非常相似。但执行以下操作不起作用: (1,2) `mappend` (3,4) 哈斯凯尔有没有办法添加两个二元组?数字是幺半群,这不是问题所在。问题是,有两种不同的、同样好的方法,它们是幺半群——加法只有一种,另一种是乘法
(\a b->(fst a+fst b,snd a+snd b))(1,2)(3,4)
简单地进行模式匹配。然而,我认为Haskell标准库应该提供一些方法来完成这样的事情。(a,b)
类型的mappend
作为幺半群的定义看起来非常相似。但执行以下操作不起作用:
(1,2) `mappend` (3,4)
哈斯凯尔有没有办法添加两个二元组?数字是幺半群,这不是问题所在。问题是,有两种不同的、同样好的方法,它们是幺半群——加法只有一种,另一种是乘法。因此,标准库决定根本不提供幺半群实例,我认为这是一个好的决定 Haskell允许newtype包装器中的打包类型在不影响基础类型的情况下选择实例。两个不同的幺半群实例在base中可用:
Prelude Data.Monoid> case (Sum 1, Sum 2)<>(Sum 3, Sum 4) of (Sum a, Sum b) -> (a,b)
(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)
甚至更简洁、更不依赖本地签名的方法也可以使用:
…然而,正如我现在相当惊讶地发现的那样,newtype
库没有提供实现此功能所必需的tuple实例。您可以自己定义它:
> :set -XFunctionalDependencies -XUndecidableInstances
> instance (Newtype a α, Newtype b β) => Newtype (a,b) (α,β) where {pack=pack***pack; unpack=unpack***unpack}
如果您确实只需要加法幺半群,我建议您使用专用类进行加法:
这也可以用一些简单、愚蠢但奇特的方法来实现(尽管这种方法需要将Haskell转换为Lisp)
如果您允许我忽略
Monoid
在您的问题中的作用,这里有两种表达它的方法,这是Applicative
到Bifunctor
的一个非常简单的概括:
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)
GHCi>导入数据。双应用
GHCi>:t bimap
bimap::Bifunctor p=>(a->b)->(c->d)->p a c->p b d
GHCi>:t()
()::双应用程序p=>p(a->b)(c->d)->p a c->p b d
GHCi>bimap(+)(+)(1,2)(3,4)
(4,6)
GHCi>:t biliftA2
胆汁2
::双应用程序w=>
(a->b->c)->(d->e->f)->w a d->w b e->w c f
GHCi>biliftA2(+)(+)(1,2)(3,4)
(4,6)
您可以使用Sum
newtype对Monoid
实例进行编码。数字通常可以属于多个Monoid
实例,因此默认情况下没有为它们定义一个实例。你必须选择你想用哪一个。我可能还建议您查看控件。Arrow
模块,因为您想做的事情可以用这些组合符很容易地描述。看起来像我的答案,但我觉得这比简单地使用lambda有点复杂:/您也不需要到处重复Sum
,因为有一个实例(Num a)=>Num(Sum a)
,因此只要不模棱两可,推理就可以得到它。例如,(Sum x,Sum y)->(x,y)@JonPurdy对,Num
实例如果元组不只是包含数字文本(这几乎肯定不是实际的用例)的case(1,2)(3,4))但是变量的类型已经是刚性的。如果调用函数,例如bisum
和getBisum
,而不是使用后缀语法,则使用的括号会少得多。
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)
> :set -XFunctionalDependencies -XUndecidableInstances
> instance (Newtype a α, Newtype b β) => Newtype (a,b) (α,β) where {pack=pack***pack; unpack=unpack***unpack}
Prelude Data.AdditiveGroup> (1,2)^+^(3,4)
(4,6)
{-# 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> (( ((1,2) +?) <> ((3,4) +?)) +!)
(4,6)
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)