簡體   English   中英

Haskell:Double 和 Int 之間的變量類型混淆

[英]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] ]

所以這可能不足為奇,因為xInt[1..x]Int的列表,所以yInt y^2Int也就不足為奇了。 GHC 失去我們的地方是它決定因此1 / (y^2)是一個Int

原因是這樣的:除法運算符的類型是

(/) :: (Fractional a) => a -> a -> a

也就是說,它在兩邊都采用相同的類型,並且也返回它。 因此,一旦y^2的類型已知為Int ,我們就推斷1和整個表達式也是Int 只是稍后,在約束檢查階段,GHC 會確保IntFractional ,當然不是。 例如:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM