(+)
和 (++)
只是 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]
(它让我做梦。)。对于坚持抽象和诸如 Haskell 之类的东西的美丽语言来说,三个甚至更多的函数对于同一件事来说并不是一件好事。
我也看到了同一种重复使用的单子(monad):
fmap
是相同的,或近,作为map
,(.)
,liftM
,mapM
,forM
,...我知道
fmap
有历史原因,但是幺半群呢? Haskell 委员会是否对此有计划?它会破坏一些代码,但我听说,虽然我不确定,有一个即将到来的版本会有很大的变化,这是一个很好的机会。太可惜了……至少, fork 买得起吗?编辑
在我读到的答案中,事实上,对于数字,
(*)
或 (+)
都可以放入 mappend
。事实上,我认为 (*)
应该是 Monoid
的一部分!看:目前,忘记了函数
mempty
和 mconcat
,我们只有 mappend
。class Monoid m where
mappend :: m -> m -> m
但我们可以这样做:
class Monoid m where
mappend :: m -> m -> m
mmultiply :: m -> m -> m
它会(也许,我还没有考虑得足够多)表现如下:
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, ...]
实际上 'mmultiply' 只会根据 'mappend' 定义,因此对于
Monoid
的实例,无需重新定义它!那么 Monoid
更接近数学;也许我们也可以将 (-)
和 (/)
添加到类中!如果这有效,我认为它会解决
Sum
和 Product
以及函数重复的情况: mappend
变为 (+)
和新的 1x231314313143131313431313134313431343131343431343131343131341基本上我建议用“上拉”重构代码。
哦,我们还需要一个新的
mmultiply
用于 (*)
。我们可以在类
mempty
中抽象这些运算符,并定义 (*)
如下: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
-...
好吧,我还不知道如何做到这一点,但我认为所有这些都有一个很酷的解决方案。
最佳答案
你试图在这里混合一些不同的概念。
算术和列表连接是非常实用的直接操作。如果你写:
[1, 2] ++ [3, 4]
...你知道你会得到
[1, 2, 3, 4]
作为结果。Monoid 是一个更抽象的数学代数概念。这意味着
mappend
不必字面意思是“将这个附加到那个;”它可以有许多其他含义。当你写:[1, 2] `mappend` [3, 4]
...这些是该操作可能产生的一些有效结果:
[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 []
为什么列表的
mappend
只是连接列表?因为这只是编写 Haskell 报告的人选择作为默认实现的幺半群的定义,可能是因为它对列表的所有元素类型都有意义。实际上,您可以通过将列表包装在各种新类型中来使用列表的替代 Monoid 实例;例如,有一个替代 Monoid 实例,用于对它们执行笛卡尔积的列表。Monoid这个概念在数学中有着固定的含义和悠久的历史,在Haskell中改变它的定义意味着偏离数学概念,这是不应该发生的。 Monoid 不仅仅是对空元素和(文字)追加/连接操作的描述;它是遵循 Monoid 提供的接口(interface)的广泛概念的基础。
您正在寻找的概念特定于数字(因为您无法定义类似
mmultiply
或 mproduce
/mproduct
之类的东西,对于 0x251812231343 的所有实例,已经存在 151812231343 的所有实例,例如 151812231343 的数学概念已经存在,18123134312313143143143131313131343134并没有真正涵盖您的问题中的结合性,但无论如何您都在示例中的不同概念之间跳跃﹘有时坚持结合性,有时不坚持﹘但总体思路是相同的)。在 Haskell 中已经有 Semirings 的实现,例如在 Semiring 包中。
然而,幺半群通常不是半环,特别是除了加法和乘法之外,对于实数还有多种半环的实现。向像 Monoid 这样定义良好的类型类添加广泛的泛化添加,不应该仅仅因为它“会很整洁”或“会节省一些击键次数”;我们将
Maybe a
、 algebra
和 (++)
作为单独的概念是有原因的,因为它们代表了完全不同的计算思想。关于Haskell:重复函数 (+) 和 (++),mappend,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10961483/