2011-11-24 112 views
6

我有以下幾點:爲什麼我不能聲明推斷的類型?

runcount :: (Eq a, Num b) => [a] -> b 
runcount = runcountacc 0 

runcountacc :: (Eq a, Num b) => b -> [a] -> b 
runcountacc n (_:[]) = runcountacc (n+1) [] 
runcountacc n (x:xs) = runcountacc (n+(if head xs==x then 0 else 1)) xs 
runcountacc n _ = n 

當我嘗試將其加載到擁抱生成該錯誤:

:6 - Cannot justify constraints in explicitly typed binding 
*** Expression : runcountacc 
*** Type   : (Eq a, Num b) => b -> [a] -> b 
*** Given context : (Eq a, Num b) 
*** Constraints : Eq c 

當加載到ghci中出現以下錯誤:

:6:23: Ambiguous type variable `a0' in the constraint: 
    (Eq a0) arising from a use of `runcountacc' 
Probable fix: add a type signature that fixes these type variable(s) 
In the expression: runcountacc (n + 1) [] 
In an equation for `runcountacc': 
    runcountacc n ([x]) = runcountacc (n + 1) [] 

但是,當刪除runcountacc的類型聲明時:

runcount :: (Eq a, Num b) => [a] -> b 
runcount = runcountacc 0 

runcountacc n (_:[]) = runcountacc (n+1) [] 
runcountacc n (x:xs) = runcountacc (n+(if head xs==x then 0 else 1)) xs 
runcountacc n _ = n 

的代碼加載罰款,並在ghci中被要求的runcountacc的類型是什麼,我們得到如下:

λ> :t runcountacc 
runcountacc :: (Num a, Eq a1) => a -> [a1] -> a 

爲什麼我不能宣佈推斷類型的runcountacc

+1

首先,擁抱是古老的,你應該使用GHc。其次,這不是一個擁抱的具體問題,你會得到一個與ghc類似的錯誤。 – HaskellElephant

+0

@HaskellElephant擁抱「古老」並不是避免它的好理由。實施仍然有效。 – singpolyma

+0

@singpolyma,是的,它本身並不是很好的理由,但是自從最新版本是從2006年開始,它不被新庫支持,它不支持haskell 2010標準。此外,許多使用擁抱的理由(例如口譯員)已經在GHC中實施。 – HaskellElephant

回答

8

我的猜測是,當你遺漏了類型簽名時,Haskell假定你不打算使用多態遞歸(對於哪種類型推斷不是那麼有效)。相應地,當您對runcountacc (n + 1) []進行遞歸調用時,列表元素類型將被視爲與原始函數調用中的相同。通常的Hindley-Milner過程很好地工作,爲runcountacc計算一個統一的單態類型,然後通過泛化自由類型變量和未解約束來形成一個類型方案。

但是,只要你寫了一個類型簽名,允許多態遞歸的可能性,當你調用runcountacc (n + 1) [],沒有理由認爲對於[]的未決定要素類型應該是什麼特別。但是,這種未確定類型仍然需要一個Eq實例來滿足runcountacc上的約束條件,並且無法確定要使用哪個Eq實例。它真的很含糊。

有很多方法可以重寫此代碼來理清這種模糊性。

+0

最好的可能是修復基礎案例,但它可能是一項家庭作業,所以提供解決方案並不好。 – nponeccop

+0

看來這不是因爲多態遞歸,而是因爲單態限制的影響:http://www.haskell.org/onlinereport/decls.html#sect4.5.5請參閱規則1(b)和第二個項目符號動機。你怎麼看? – nponeccop

8

爲什麼我不能在代碼中編寫runco​​untacc的推斷類型?

簡短的回答是,因爲您錯誤地創建了多態遞歸,並且如果存在多態遞歸,那麼類型推斷根本不應該工作。

GHC提供了一個更好的錯誤信息:

orig.hs:5:24: 
    Ambiguous type variable `a0' in the constraint: 
     (Eq a0) arising from a use of `runcountacc' 
    Probable fix: add a type signature that fixes these type variable(s) 
    In the expression: runcountacc (n + 1) [] 
    In an equation for `runcountacc': 
     runcountacc n (_ : []) = runcountacc (n + 1) [] 

在那裏,它不能推斷右側[]類型。下面的簽名解決了這個問題,因爲沒有它不是應該用什麼清空列表:

runcountacc n (_:[]) = runcountacc (n+1) ([] :: [a]) 

我們有一種(無限)態遞歸這裏。右側空列表的類型可以是任何東西,而GHC不能理解哪一個。例如,下面仍然是有效的:

runcountacc n (_:[]) = runcountacc (n+1) ([] :: [String]) 

爲什麼沒有類型簽名問題消失的問題保持開放,但。

@pigworker的想法是,如果你省略簽名,Haskell不允許多態遞歸,並且單形拉伸沒有歧義。

注意:你得到的遞歸基本情況是錯誤的 - 一個無限循環肯定不會出現在第一位。

相關問題