2012-06-06 35 views
2

我是來自C++和Java背景的Haskell的新手。有時候,我對Haskell的類型系統有困難。我現在的錯誤是這段代碼:允許綁定的模糊類型變量

countIf :: (Integral b) => [a] -> (a -> Bool) -> b 
countIf [] p = 0 
countIf (x:xs) p 
    | p x = 1 + countIf xs p 
    | otherwise = countIf xs p 

isRelativelyPrime :: (Integral a) => a -> a -> Bool 
isRelativelyPrime m n = gcd m n == 1 

phi :: (Integral a, Integral b) => a -> b 
phi n = countIf [1..(n - 1)] (isRelativelyPrime n) 

main = print [(n, phi n, ratio) | n <- [1..10], let ratio = (fromIntegral (phi n))/n] 

的錯誤消息是

prog.hs:13:60: 
    Ambiguous type variable `b' in the constraints: 
     `Fractional b' arising from a use of `/' at prog.hs:13:60-85 
     `Integral b' arising from a use of `phi' at prog.hs:13:75-79 
    Probable fix: add a type signature that fixes these type variable(s) 

13:60只是fromIntegral在讓主我的名單理解結合使用之前。我仍然在嘗試習慣ghc的錯誤信息。我無法破譯這個特定的一個,以找出我需要改變以獲得我的代碼進行編譯。任何幫助將不勝感激。謝謝。

+2

您的'countIf'可以定義爲標準函數的組合:'countIf = length。翻轉過濾器'。 – demi

回答

3

你需要從n上調用fromIntegral,因爲Haskell不會自動從整型類型轉換而來,因爲你從fromIngral(phi n)調用了這些類型。我一直犯這個錯誤,沒有什麼大不了的!

+0

謝謝,那是我錯過的細節。 –

6

這是一個常見的初學者錯誤的例子:過度多態代碼

您已儘可能地使您的代碼儘可能通用,例如

phi :: (Integral a, Integral b) => a -> b 

這將需要任何整型任何其他整數類型,通過phi轉型。

這樣的多態代碼對圖書館來說很好,但對於類型推斷來說不是那麼好。我會把錢放在Integers上,所以我們可以繼續提供更準確的類型,

countIf :: [Integer] -> (Integer -> Bool) -> Integer 
countIf [] p = 0 
countIf (x:xs) p 
    | p x  = 1 + countIf xs p 
    | otherwise = countIf xs p 

isRelativelyPrime :: Integer -> Integer -> Bool 
isRelativelyPrime m n = gcd m n == 1 

phi :: Integer -> Integer 
phi n = countIf [1..(n - 1)] (isRelativelyPrime n) 

main = print [ (n, phi n, ratio) 
      | n <- [1..10], let ratio = (fromIntegral (phi n)) ] 

和類型錯誤就會消失。

你甚至可以看到性能的提高(特別是如果你專注於Int)。

+0

我個人反對向新手推薦'Int',但+1將過度多態性視爲一個問題:) –

+0

@DonStewart感謝您對過度多態性的建議。在這一點上,我可能確實已經過度了。但是,該錯誤不會隨着該更改而消失。您的代碼在「let ratio = ...」綁定中(因此名稱爲「ratio」)沒有發生錯誤消息的位置。上面的Max的答案解決了錯誤,因爲我需要一個「fromInteger n」來完成浮點除法。 –

+0

@benmachine你爲什麼反對爲新手推薦Int's? –