2011-06-19 68 views
72

有人能告诉我为什么Haskell序列定义了两个单幂函数(即^**)?我认为类型系统应该消除这种重复。Haskell中的幂指数

Prelude> 2^2 
4 
Prelude> 4**0.5 
2.0 

回答

107

这样共有三种幂运算符:(^)(^^)(**)^是非负整数幂,^^是整数幂,和**是浮点幂:

(^) :: (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类型**封闭。由于Int没有Fractional实例,因此无法将其提升为负值。

理想情况下,^的第二个参数将被静态限制为非负数(目前,1^(-2)引发运行时异常)。但Prelude中没有自然数字的类型。

8

它没有定义两个运算符 - 它定义了三个!从报告:

有三两个参数幂操作:(^)提出任何数量的非负整数次幂,(^^)抛出一个小数任何整数次幂,和(**)有两个浮动点参数。对于任何xx^0x^^0的值为1,包括零; 0**y未定义。

这意味着有三个不同的算法,其中两个得到准确的结果(^^^),而**给出近似的结果。通过选择要使用的运算符,您可以选择要调用的算法。

4

^要求其第二个参数是Integral。如果我没有弄错,如果你知道你正在使用一个整数指数,那么实现会更有效率。另外,如果你想要类似2^(1.234)的东西,即使你的基数是一个整数,2,你的结果显然是小数。你有更多的选择,这样你可以更严格地控​​制哪些类型进出指数函数。

Haskell的类型系统与其他类型的系统(如C,Python或Lisp)没有相同的目标。鸭子打字(几乎)与Haskell思维模式相反。

+4

我不完全同意Haskell类型的思维方式与鸭打字相反。 Haskell类型类很像鸭子打字。 '鸭子嘎嘎嘎嘎:: a - >嘎嘎'定义了我们对鸭子的期望,然后每个实例指定了一些可以像鸭子一样行事的东西。 – augustss

+7

@augustss我明白你来自哪里。但鸭子打字背后的非正式的座右铭是“如果它看起来像鸭子,像鸭子一样,像鸭子一样呱呱叫,那么它就是一只鸭子。”在Haskell中,除非它被声明为'Duck'的实例,否则它不是鸭子。 –

+1

的确如此,但这正是我对Haskell的期望。你可以做任何你想吃鸭子的东西,但是你必须明确地说明它。我们不想把我们没有要求的东西误认为是鸭子。 – augustss

26

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 ** 

此,如果你打开多参数类型的类扩展并未真正发挥作用,甚至,因为实例选择需要更加聪明比哈斯克尔目前允许。

+3

关于这个不能实现的陈述仍然正确吗? IIRC,haskell可以选择多参数类型类的第二个参数,严格按照第一个参数确定。除此之外还有另外一个无法解决的问题吗? – RussellStewart

+1

@singular这仍然是事实。例如,第一个参数不会确定第二个参数,例如,您希望指数既是“Int”又是“Integer”。为了能够拥有这三个实例声明,实例解析必须使用回溯,并且没有Haskell编译器实现这一点。 – augustss

+4

_“类型系统不够强大”_参数仍然持有截至2015年3月? –