你得到的錯誤告訴你它認爲該類型應該是什麼;不幸的是,這兩種類型都是用類型變量表示的,這使得它更難以看清。第一行表示你給出了n
這個表達式,但是它想給它類型n1
。爲了弄清楚這是什麼意思,看接下來的幾行:
`n1' is a rigid type variable bound by
the type signature for `width' at Dungeon.hs:11:16
這是說n1
是一種變量,其值是已知的,因此不能改變(是「剛性」)。由於它受width
類型簽名的約束,因此您知道它受限於行width :: (Num n) => a -> n
。在範圍內還有另一個n
,所以此n
被重命名爲n1
(width :: (Num n1) => a -> n1
)。接下來,我們
`n' is a rigid type variable bound by
the instance declaration at Dungeon.hs:13:14
這是告訴你,哈斯克爾發現從線instance (Num n) => HasArea (Room n) where
類型n
。正在報告的問題是n
,這是爲width (Room w h) = w
計算的GHC類型,與n1
不同,它是預期的類型。
你遇到這個問題的原因是你的width
的定義比預期的多態性少。 width
的類型簽名是(HasArea a, Num n1) => a -> n1
,這意味着對於HasArea
的一個實例,您可以用任何類型的數字來表示它的寬度。但是,在您的實例定義中,行width (Room w h) = w
意味着width
的類型爲Num n => Room n -> n
。請注意,這不是足夠多態的:Room n
是HasArea
的一個實例,這將需要width
的類型爲(Num n, Num n1) => Room n -> n1
。這是無法統一具體的n
與一般n1
這是導致您的類型錯誤。
有幾種方法可以解決這個問題。一種方法(也許是最好的方法),你可以在sepp2k's answer中看到的是使HasArea
取類型變量* -> *
;這意味着,而不是a
本身是一種類型,例如a Int
或a n
是類型。 Maybe
和[]
是類型爲* -> *
的示例。 (普通類型如Int
或Maybe Double
有種類*
。)這可能是最好的選擇。
如果你有一些類型的一種*
其中有一個區域(例如,data Space = Space (Maybe Character)
,其中width
總是1
),但是,這是行不通的。另一種方法(其需要一些擴展Haskell98/Haskell2010)是使HasArea
多參數類型的類:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
data Room n = Room n n deriving Show
class Num n => HasArea a n where
width :: a -> n
instance Num n => HasArea (Room n) n where
width (Room w h) = w
現在,傳遞寬度的類型作爲參數的類型類本身,所以width
有類型(HasArea a n, Num n) => a -> n
。但是,這可能會有一個缺點,那就是你可以聲明instance HasArea Foo Int
和instance HasArea Foo Double
,這可能有問題。如果是,那麼爲了解決這個問題,你可以使用函數依賴或類型族。函數依賴允許你指定給定的類型,其他類型是唯一確定的,就像你有一個普通的函數一樣。使用這些給人的代碼
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}
data Room n = Room n n deriving Show
class Num n => HasArea a n | a -> n where
width :: a -> n
instance Num n => HasArea (Room n) n where
width (Room w h) = w
的位告訴GHC,如果它可以推斷a
,那麼它也可以推斷n
,因爲只有一個n
爲每a
。這可以防止上面討論的那種情況。
類型的家庭都更不同:
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, TypeFamilies #-}
data Room n = Room n n deriving Show
class Num (Area a) => HasArea a where
type Area a :: *
width :: a -> Area a
instance Num n => HasArea (Room n) where
type Area (Room n) = n
width (Room w h) = w
這是說,除了具有width
功能,HasArea
類也有一個Area
型(或類型的功能,如果你要考慮一下那樣)。對於每個HasArea a
,指定Area a
類型(其中,由於超類約束,必須是Num
的實例),然後使用該類型作爲您的數字類型。
至於如何調試這樣的錯誤?老實說,我最好的建議是「練習,練習,練習」。隨着時間的推移,你會得到更多使用,以搞清楚(一)中的錯誤在說什麼,和(b)什麼可能出了問題。隨機更換東西是做這種學習的一種方式。建議我可以給最大的一塊,不過,是要注意Couldn't match expected type `Foo' against inferred type `Bar'
線。這些告訴你的編譯器計算什麼(Bar
)和預期(Foo
)的類型,如果你能準確地找出那些是哪種類型,可以幫助你找出錯誤所在。
哇,你真把那走出公園。我在Google上搜索這些確切的主題,因爲我立即發現缺乏將它用於多種類型的功能。不僅如此,我錯過了迄今爲止最簡單的多參數類型類。謝謝。 – 2011-01-11 02:33:07