Haskell: 为什么 (+), (-) 是 Num 类型类的一部分?
Haskell: Why is (+), (-) part of Num typeclass?
来自 ghci:
Prelude> :i Num
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
-- Defined in `GHC.Num'
instance Num Word -- Defined in `GHC.Num'
instance Num Integer -- Defined in `GHC.Num'
instance Num Int -- Defined in `GHC.Num'
instance Num Float -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'
为什么 Num class 的 (+)
、(-)
部分以开头?
例如 - 您可以轻松定义此类型 class:
class Plus a where
(+) :: a -> a -> a
然后让:
instance Plus [] where
(+) = (++)
你还可以为集合定义这些表示集合并集,或者将 (-)
添加到类型 class 表示集合差异......定义 [=17 是没有意义的=] 在列表中。
当然我可以创建自己的类型 class 来代替 (|+|)
- 但为什么这些运算符在 haskell 中只为 Num 保留?
那么为什么做出这个选择呢?是因为遗留问题还是没有人想要这个?
很多是历史原因,但也有数学原因。例如,已经有支持二元运算符的结构的数学名称。最常用的是Monoid,你可以在Data.Monoid
中使用。此类型类定义了一个函数 mappend
和一个值 mempty
,相当于 mappend
的标识元素,并且 mappend
有一个运算符别名 <>
.列表和许多其他对象形成幺半群,而数字实际上形成 2,具有 +
和 *
,其中单位元素分别为 0 和 1。具有恒等式、关联二元运算和该运算的逆运算(例如,减法是加法的逆运算)的结构称为组(不是标准库的一部分),并且在一个运算符下形成组的结构和第二个运算符下的幺半群称为环。这些对象是代数 structures/abstract 代数 类.
的基础
这些数学结构在 Haskell 中实施起来有些棘手,至少非常好。 Monoid
与 +
和 *
的所有 Num
类型存在重叠实例,并且对于某些数字类型,如果 /
则存在 Group
重叠可以定义为除以 0 是明确定义的(某些结构可以允许这样做)。这些重叠的实例导致许多新类型,使得日常工作变得困难。 Num
类型类在这里有所帮助,因为它提供了一个有用的接口,即对数字进行操作和执行操作的接口,它很容易在现实世界的代码中使用,而不仅仅是在学术界。已经有人尝试引入更数学化的 Prelude
版本,一些人使用这些版本并取得了不同程度的成功,但您的普通 Haskell 用户宁愿放弃纯粹的数学,转而使用更实用的界面。
简而言之,Num
类型类以这种方式定义是出于历史原因,但也出于非常实际的原因。更严格的数学构造是笨拙的,对于许多类型,简单地使用 Data.Monoid
的 <>
运算符是完美的。
我的猜测是他们打算 Num
反映数字的数学性质,而不是一些更一般的结构。 Num
中的操作主要是那些支持称为 ring 的抽象数学结构的操作,其中整数(及其任何扩展)是一个突出的例子。
由于 Haskell 允许您定义自己的运算符,因此您可以设置自己的符号。然后,当其他人阅读您的代码时,他们将能够判断出您正在使用的东西不是 Num
ber-like,但可能更通用。
Haskell序曲中有许多不幸的历史设计决定,无法修复,因为它们会破坏兼容性。但是,恕我直言,Num
类型类和运算符的基本设计是合理的。
来自 ghci:
Prelude> :i Num
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
-- Defined in `GHC.Num'
instance Num Word -- Defined in `GHC.Num'
instance Num Integer -- Defined in `GHC.Num'
instance Num Int -- Defined in `GHC.Num'
instance Num Float -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'
为什么 Num class 的 (+)
、(-)
部分以开头?
例如 - 您可以轻松定义此类型 class:
class Plus a where
(+) :: a -> a -> a
然后让:
instance Plus [] where
(+) = (++)
你还可以为集合定义这些表示集合并集,或者将 (-)
添加到类型 class 表示集合差异......定义 [=17 是没有意义的=] 在列表中。
当然我可以创建自己的类型 class 来代替 (|+|)
- 但为什么这些运算符在 haskell 中只为 Num 保留?
那么为什么做出这个选择呢?是因为遗留问题还是没有人想要这个?
很多是历史原因,但也有数学原因。例如,已经有支持二元运算符的结构的数学名称。最常用的是Monoid,你可以在Data.Monoid
中使用。此类型类定义了一个函数 mappend
和一个值 mempty
,相当于 mappend
的标识元素,并且 mappend
有一个运算符别名 <>
.列表和许多其他对象形成幺半群,而数字实际上形成 2,具有 +
和 *
,其中单位元素分别为 0 和 1。具有恒等式、关联二元运算和该运算的逆运算(例如,减法是加法的逆运算)的结构称为组(不是标准库的一部分),并且在一个运算符下形成组的结构和第二个运算符下的幺半群称为环。这些对象是代数 structures/abstract 代数 类.
这些数学结构在 Haskell 中实施起来有些棘手,至少非常好。 Monoid
与 +
和 *
的所有 Num
类型存在重叠实例,并且对于某些数字类型,如果 /
则存在 Group
重叠可以定义为除以 0 是明确定义的(某些结构可以允许这样做)。这些重叠的实例导致许多新类型,使得日常工作变得困难。 Num
类型类在这里有所帮助,因为它提供了一个有用的接口,即对数字进行操作和执行操作的接口,它很容易在现实世界的代码中使用,而不仅仅是在学术界。已经有人尝试引入更数学化的 Prelude
版本,一些人使用这些版本并取得了不同程度的成功,但您的普通 Haskell 用户宁愿放弃纯粹的数学,转而使用更实用的界面。
简而言之,Num
类型类以这种方式定义是出于历史原因,但也出于非常实际的原因。更严格的数学构造是笨拙的,对于许多类型,简单地使用 Data.Monoid
的 <>
运算符是完美的。
我的猜测是他们打算 Num
反映数字的数学性质,而不是一些更一般的结构。 Num
中的操作主要是那些支持称为 ring 的抽象数学结构的操作,其中整数(及其任何扩展)是一个突出的例子。
由于 Haskell 允许您定义自己的运算符,因此您可以设置自己的符号。然后,当其他人阅读您的代码时,他们将能够判断出您正在使用的东西不是 Num
ber-like,但可能更通用。
Haskell序曲中有许多不幸的历史设计决定,无法修复,因为它们会破坏兼容性。但是,恕我直言,Num
类型类和运算符的基本设计是合理的。