Haskell:重复的函数(+)和(++),mappend

L01*_*man 11 monads haskell operator-overloading typeclass monoids

(+)而且(++)只是专业化mappend; 我对吗?他们为什么需要?这是无用的重复,因为Haskell具有这些强大的类型类和推断.比方说,我们删除(+)(++)重命名mappend (+)为直观方便并输入增益.对于初学者来说,编码会更直观,更短,更容易理解:

--old and new
1 + 2
--result
3

--old
"Hello" ++ " " ++ "World"
--new
"Hello" + " " + "World"
--result
"Hello World"

--old
Just [1, 2, 3] `mappend` Just [4..6]
--new
Just [1, 2, 3] + Just [4..6]
--result
Just [1, 2, 3, 4, 5, 6]
Run Code Online (Sandbox Code Playgroud)

(这让我梦想.)对于一个坚持抽象的美丽语言和Haskell之类的东西来说,同一个函数中的三个,甚至更多函数并不是一件好事.我也看到了相同的一种单子重复:fmap是相同的,或近,如map,(.),liftM,mapM,forM,...我知道有对史传的原因fmap,但什么幺?Haskell委员会是否正在计划这方面的事情?它会破坏一些代码,但我听说,虽然我不确定,但是有一个传入的版本会有很大的变化,这是一个很好的场合.太可惜了......至少,叉子价格合理吗?

编辑 在回答我看了,有一个数字,无论是事实(*)还是(+)可以适应mappend.事实上,我认为(*)应该是其中的一部分Monoid!看:

目前,关于功能memptymconcat我们只有mappend.

class Monoid m where
    mappend :: m -> m -> m
Run Code Online (Sandbox Code Playgroud)

但我们可以这样做:

class Monoid m where
    mappend :: m -> m -> m
    mmultiply :: m -> m -> m
Run Code Online (Sandbox Code Playgroud)

它(可能,我还没有足够的关于它)表现如下:

3 * 3
mempty + 3 + 3 + 3
0 + 3 + 3 + 3
9

Just 3 * Just 4
Just (3 * 4)
Just (3 + 3 + 3 +3)
Just 12

[1, 2, 3] * [10, 20, 30]
[1 * 10, 2 * 10, 3 * 10, ...]
[10, 20, 30, 20, 40, 60, ...]
Run Code Online (Sandbox Code Playgroud)

实际上'mmultiply'只能用'mappend'来定义,所以对于Monoid没有必要重新定义它的例子!然后Monoid更接近数学; 也许我们可以如加(-)(/)的类!如果一切正常,我认为这将解决的情况下Sum,并Product还有职能重复:mappend变得(+)和新的mmultiply仅仅是(*).基本上我建议用"拉起"来重构代码.哦,我们还需要一个新mempty(*).我们可以在类中抽象这些运算符MonoidOperator并定义Monoid如下:

class (Monoid m) => MonoidOperator mo m where
    mempty :: m
    mappend :: m -> m -> m

instance MonoidOperator (+) m where
    mempty = 0
    mappend = --definition of (+)

instance MonoidOperator (*) where
    --...

class Monoid m where
    -...
Run Code Online (Sandbox Code Playgroud)

好吧,我不知道该怎么做但我认为所有这一切都有一个很酷的解决方案.

dfl*_*str 11

你试图在这里混合一些独立的概念.

算术和列表连接是非常实用的直接操作.如果你写:

[1, 2] ++ [3, 4]
Run Code Online (Sandbox Code Playgroud)

......你知道你会得到[1, 2, 3, 4]结果.


一个含半幺群是一个数学代数的概念,是一个更抽象的层次.这意味着mappend不必字面意思是"将其附加到那个"; 它可以有许多其他含义.当你写:

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

......这些是该操作可能产生的一些有效结果:

[1, 2, 3, 4] -- concatenation, mempty is []

[4, 6]       -- vector addition with truncation, mempty is [0,0..]

[3, 6, 4, 8] -- some inner product, mempty is [1]

[3, 4, 6, 8] -- the cartesian product, mempty is [1]

[3, 4, 1, 2] -- flipped concatenation, mempty is []

[]           -- treating lists like `Maybe a`, and letting lists that
             -- begin with positive numbers be `Just`s and other lists
             -- be `Nothing`s, mempty is []
Run Code Online (Sandbox Code Playgroud)

为什么mappend列表只是连接列表?因为这只是monoids的定义,编写Haskell报告的人选择作为默认实现,可能是因为它对列表的所有元素类型都有意义.实际上,您可以通过将它们包装在各种新类型中来为列表使用替代的Monoid实例; 例如,对于在其上执行笛卡尔积的列表,存在替代的Monoid实例.

"Monoid"的概念在数学中具有固定的含义和悠久的历史,在Haskell中改变其定义意味着偏离数学概念,这不应该发生.Monoid不仅仅是对空元素和(文字)追加/连接操作的描述; 它是遵循Monoid提供的界面的广泛概念的基础.


您正在寻找的概念是特定于数字的(因为您无法定义类似mmultiply或可能mproduce/ 例如mproduct所有实例Maybe a),这个概念已经存在并被称为数学中的半环(嗯,您没有真正覆盖你问题中的相关性,但是你在你的例子中不同的概念之间跳跃,有时候会坚持相关性,有时候不是这样,但总体思路是相同的.

Haskell中已经有Semirings的实现,例如在algebra包中.

然而,Monoid通常不是半环形,并且除了加法和乘法之外,还存在用于实数的半环的多个实现.为Monoid这样非常明确定义的类型类添加广泛的通用加法不应仅仅因为它"整齐"或"会节省一些键击"而完成.我们有一个原因(++),(+)并且mappend作为单独的概念,因为它们代表完全不同的计算思想.


dor*_*ard 9

将mappend重命名为(+)/(*)

虽然(+)(*)均类群它们具有附加的分配性的法律,与这两个操作,以及法律例如0*x = 0的本质上,(+)(*)形成一个.某些其他类型的两个幺半群可能不满足这些环(或甚至较弱的半环)属性.操作符的命名(+)(*)暗示它们的附加(相互关联)属性.因此,我会避免通过重命名mappend来破坏传统的数学直觉,+或者*因为这些名称暗示了可能不具备的附加属性.有时过多的过载(即过多的泛化)会导致直觉的丧失,从而导致可用性的丧失.

如果你碰巧有两个形成某种环的幺半群,那么你可能想Num从这些中得到一个实例,因为名称" +"和" *"表示其他属性.

关于conflating(++)和mappend

重命名mappend,以(++)作为有较少的额外精神包袱可能更合适(++).实际上,由于列表是自由的monoid(即没有附加属性的monoid),因此使用传统的list-concatentation运算符(++)来表示monoid的二元运算似乎并不是一个糟糕的主意.

关于为一种类型定义多个幺半群

正如你所指出的,两者(+)都是(*)幺半群,但两者都不能成为Monoid同一类型的实例t.您提供的一个解决方案是为类提供一个额外的类型参数Monoid来区分两个幺半群.请注意,类型类只能按类型进行参数化,而不能按照您在问题中显示的表达式进行参数化.一个合适的定义是这样的:

class Monoid m variant where
 mappend :: variant -> m -> m -> m
 mempty :: variant -> m

data Plus = Plus
data Times = Times

instance Monoid Int Plus where
    mappend Plus x y = x `intPlus` y
    mempty = 0

instance Monoid Int Times where
    mappend Times x y = x `intTimes` y
    mempty = 1

(+) = mappend Plus
(*) = mappend Times 
Run Code Online (Sandbox Code Playgroud)

为了将mappend/的应用程序mempty解析为特定操作,每个必须采用一个值来表示指示特定幺​​半群"变体"的类型.

旁白:你问题的标题提到了mconcat.这是一个完全不同的操作mappend- mconcat是从自由幺半群到其他幺半群的幺半群同态,即用mappend 取代cons,用mempty 取代nil.