[英]Haskell rules for selecting between type instances for Int, Double and Integer
[英]Haskell: Variable Type confusion between Double and Int
作為一個 uni assignment,我應該寫一個 function 類型聲明:
pi_approx :: Int -> Double
這是我的第一次嘗試:
pi_approx :: Int -> Double
pi_approx x = let total = sum [1 / (y^2) | y <- [1..x]]
in sqrt (6 * total)
這引發了以下錯誤:
pi_approx.hs:4:8: error:
* Couldn't match expected type `Double' with actual type `Int'
* In the expression: sqrt (6 * total)
In the expression:
let total = sum [1 / (y ^ 2) | y <- ...] in sqrt (6 * total)
In an equation for `pi_approx':
pi_approx x = let total = sum ... in sqrt (6 * total)
|
4 | in sqrt (6 * total)
| ^^^^^^^^^^^^^^^^
我一步一步地試圖理解為什么解釋器把它當作一個 Int:
level1 :: Fractional a => a -> a
level1 x = 1 / (x^2)
到目前為止,一切都很好。
level2 :: (Enum a, Fractional a) => a -> [a]
level2 x = [level1 y | y <- [1..x]]
也如預期。
level3 :: (Enum a, Fractional a) => a -> a
level3 x = sum (level2 x)
看不到 Int...
level4 :: (Enum a, Fractional a) => a -> a
level4 x = 6 * (level3 x)
最后
level5 :: (Floating a, Enum a) => a -> a
level5 x = sqrt (level4 x)
走下這個兔子洞后,我離我的答案更近了。 我讓它與fromIntegral
一起運行
pi_approx :: Int -> Double
pi_approx x = let total = sum [(fromIntegral 1) / ((fromIntegral y)^ (fromIntegral 2)) | y <- [1..x]]
in sqrt (6*total)
但這讓我更接近於理解是什么導致了第一個版本中的錯誤。 有人可以解釋我錯過了什么嗎? 為什么sqrt (6*total)
被視為Int
?
由於類型類的類型檢查方式,顯示的類型錯誤並不是最有用的錯誤。
[ 1 / (y^2) | y <- [1..x] ]
所以這可能不足為奇,因為x
是Int
, [1..x]
是Int
的列表,所以y
是Int
。 y^2
是Int
也就不足為奇了。 GHC 失去我們的地方是它決定因此1 / (y^2)
是一個Int
。
原因是這樣的:除法運算符的類型是
(/) :: (Fractional a) => a -> a -> a
也就是說,它在兩邊都采用相同的類型,並且也返回它。 因此,一旦y^2
的類型已知為Int
,我們就推斷1
和整個表達式也是Int
。 只是稍后,在約束檢查階段,GHC 會確保Int
是Fractional
,當然不是。 例如:
recp :: Int -> Int
recp x = 1 / x
會給你一個更好的錯誤
• No instance for (Fractional Int) arising from a use of ‘/’
• In the expression: 1 / x
In an equation for ‘recp’: recp x = 1 / x
但是在檢查您的 function 時並沒有那么遠,因為首先存在類型統一失敗。
有時,如果您無法找出錯誤,刪除簽名並查看推斷出的類型會很有幫助(通常,添加更多類型簽名會有所幫助,但並非總是如此)
ghci> :t pi_approx
pi_approx :: (Floating a, Enum a) => a -> a
如果沒有簽名,function 實際上會進行類型檢查。 它也有效:
ghci> pi_approx 100
3.1320765318091053
這里100
默認為Double
以滿足Fractional
約束,並且整個定義都通過Double
s。
只是它不接受Int
s:
ghci> pi_approx (100 :: Int)
<interactive>:1:1: error:
• No instance for (Floating Int) arising from a use of ‘pi_approx’
• In the expression: pi_approx (1 :: Int)
您定義的所有級別函數都具有類型a -> a
(或a -> [a]
),並且附加了一些對a
約束。 所以在所有情況下,出現的類型將是進入的類型(或其列表)。
因此,當調用每個level
函數時,有兩種可能的結果:調用是錯誤類型的,因為參數的類型不符合約束,或者您將返回同一時間的值(或其列表) . 無論哪種方式,您都不可能放入Int
並取回Float
。
因此sqrt (6*total)
被視為Int
,因為正如我們剛剛討論的那樣,輸出的類型將與輸入的類型相同,而輸入的類型是Int
。 現在進來的類型實際上並沒有滿足約束,所以你可能會想到一個錯誤。 如果沒有類型簽名,您將得到該錯誤(一旦使用Int
類型的參數調用 function )。 但是使用類型簽名,在檢查約束之前檢測到有關返回類型不匹配的錯誤,這就是您收到該錯誤的原因。
每個人都解釋了為什么它是錯誤的,但沒有人告訴你如何解決它。
這是您修復代碼以使其正常工作的方法
pi_approx :: Int -> Double
pi_approx x = let total = sum [1 /(fromIntegral (y^2)) | y <- [1..x]]
in sqrt (6 * total)
一旦你添加fromIntegral
類型檢查器就會得到滿足。 正如其他用戶所解釋的,function /
的類型不被滿足。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.