有人可以告诉我为什么Haskell Prelude为求幂定义了两个独立的函数(即^
和**
)?我认为类型系统应该消除这种重复。
Prelude> 2^2
4
Prelude> 4**0.5
2.0
答案 0 :(得分:119)
实际上有三个取幂运算符:(^)
,(^^)
和(**)
。 ^
是非负积分求幂,^^
是整数求幂,**
是浮点求幂:
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a
原因是类型安全:数值运算的结果通常与输入参数具有相同的类型。但是,您无法将Int
提升为浮点幂并获得Int
类型的结果。因此,类型系统会阻止您执行此操作:(1::Int) ** 0.5
会产生类型错误。 (1::Int) ^^ (-1)
也是如此。
另外一种方法:Num
类型在^
下关闭(它们不需要有乘法反转),Fractional
类型在^^
下关闭, Floating
类型已在**
下关闭。由于Fractional
没有Int
个实例,因此无法将其提升为负值。
理想情况下,^
的第二个参数将被静态约束为非负数(当前,1 ^ (-2)
会抛出运行时异常)。但是Prelude
中没有自然数字的类型。
答案 1 :(得分:28)
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当前允许的更聪明。
答案 2 :(得分:10)
它没有定义两个运算符 - 它定义了三个!来自报告:
有三个双参数取幂运算:(
^
)将任意数字提升为非负整数幂,(^^
)将小数提升为任何整数幂,(**
})采用两个浮点参数。对于任何x^0
,x^^0
或x
的值均为1,包括零;0**y
未定义。
这意味着有三种不同的算法,其中两种算法会给出精确的结果(^
和^^
),而**
会给出近似的结果。通过选择要使用的运算符,您可以选择要调用的算法。
答案 3 :(得分:4)
^
要求其第二个参数为Integral
。如果我没有弄错的话,如果你知道你正在使用积分指数,那么实现可以更有效率。此外,如果你想要像2 ^ (1.234)
这样的东西,即使你的基数是一个积分,2,你的结果显然是分数。您有更多选项,以便您可以更加严格地控制进入和退出指数函数的类型。
Haskell的类型系统与其他类型系统没有相同的目标,例如C,Python或Lisp。鸭子打字(几乎)与哈斯克尔心态相反。