有人能告诉我为什么 Haskell Prelude 定义了两个独立的求幂函数(即^
和**
)吗?我认为类型系统应该消除这种重复。
Prelude> 2^2
4
Prelude> 4**0.5
2.0
有人能告诉我为什么 Haskell Prelude 定义了两个独立的求幂函数(即^
和**
)吗?我认为类型系统应该消除这种重复。
Prelude> 2^2
4
Prelude> 4**0.5
2.0
实际上有三个幂运算符(^)
:(^^)
和(**)
。^
是非负整数幂,^^
是整数幂,**
是浮点幂:
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a
原因是类型安全:数值运算的结果通常与输入参数具有相同的类型。但是您不能将 an 提高Int
到浮点幂并得到 type 的结果Int
。所以类型系统会阻止你这样做:(1::Int) ** 0.5
产生类型错误。也是如此(1::Int) ^^ (-1)
。
另一种说法是:Num
类型在下封闭^
(它们不需要有乘法逆),Fractional
类型在 下封闭^^
,Floating
类型在 下封闭**
。由于没有 的Fractional
实例Int
,因此您不能将其提升为负幂。
理想情况下, 的第二个参数^
将静态约束为非负数(当前,1 ^ (-2)
引发运行时异常)。但 . 中没有自然数的类型Prelude
。
Haskell 的类型系统不足以将三个幂运算符表示为一个。你真正想要的是这样的:
class Exp a b where (^) :: a -> b -> a
instance (Num a, Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a, Floating b) => Exp a b where ... -- current **
即使您打开多参数类型类扩展,这也不起作用,因为实例选择需要比 Haskell 当前允许的更聪明。
它没有定义两个运算符——它定义了三个!来自报告:
有三个双参数求幂运算:(
^
) 将任何数字提升到非负整数幂,(^^
) 将小数提升到任何整数幂,并且 (**
) 采用两个浮点参数。x^0
对于 any , or的值为x^^0
1x
,包括零;0**y
未定义。
这意味着存在三种不同的算法,其中两种给出精确结果(^
和^^
),而**
给出近似结果。通过选择要使用的运算符,您可以选择要调用的算法。
^
requires its second argument to be an Integral
. If I'm not mistaken, the implementation can be more efficient if you know you are working with an integral exponent. Also, if you want something like 2 ^ (1.234)
, even though your base is an integral, 2, your result will obviously be fractional. You have more options so that you can have more tight control over what types are going in and out of your exponentiation function.
Haskell's type system does not have the same goal as other type systems, such as C's, Python's, or Lisp's. Duck typing is (nearly) the opposite of the Haskell mindset.