2011-06-19 33 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月? –