谁能告诉我为什么Haskell Prelude定义两个单独的求幂函数(即^
和**
)?我认为类型系统应该消除这种重复。
Prelude> 2^2
4
Prelude> 4**0.5
2.0
Answers:
这样共有三种幂运营商:(^)
,(^^)
和(**)
。^
是非负整数幂,^^
是整数幂,并且**
是浮点幂:
(^) :: (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当前允许的更聪明。
Int
和Integer
。为了能够具有这三个实例声明,实例解析必须使用回溯,并且没有Haskell编译器实现。
^
要求其第二个参数为Integral
。如果我没记错的话,如果您知道自己使用的是整数指数,则实现会更加高效。另外,如果您想要类似的东西2 ^ (1.234)
,即使您的底数是2的整数,您的结果显然也将是分数。您有更多选择,以便可以更严格地控制要输入和输出幂函数的类型。
Haskell的类型系统与其他类型系统(例如C,Python或Lisp)的目标不同。鸭子打字(几乎)与Haskell思维方式相反。
class Duck a where quack :: a -> Quack
定义我们对鸭子的期望,然后每个实例都指定一些行为类似于鸭子的行为。
Duck
。