2011-01-11 98 views
11

我非常理解3/4語言的其餘部分,但每次我在我的代碼中以有意義的方式使用類時,我都會受到永久的根深蒂固。我似乎無法找出與類混合的類型變量

爲什麼這不是非常簡單的代碼工作?

data Room n = Room n n deriving Show 

class HasArea a where 
    width :: (Num n) => a -> n 

instance (Num n) => HasArea (Room n) where 
    width (Room w h) = w 

所以,房間寬度由整數表示也許花車,我不想在這一點上,以限制它。無論是類和實例限制n型,以訂購數量,但它仍然不喜歡它,我得到這個錯誤:

Couldn't match expected type `n1' against inferred type `n' 
    `n1' is a rigid type variable bound by 
     the type signature for `width' at Dungeon.hs:11:16 
    `n' is a rigid type variable bound by 
     the instance declaration at Dungeon.hs:13:14 
In the expression: w 
In the definition of `width': width (Room w h) = w 
In the instance declaration for `HasArea (Room n)' 

所以它告訴我的類型不匹配,但是它沒有告訴我它認爲他們是什麼類型,這將是非常有用的。作爲一個方面說明,有沒有簡單的方法來調試這樣的錯誤?我知道做這件事的唯一方法是隨機更換東西,直到它工作。

回答

18

你得到的錯誤告訴你它認爲該類型應該是什麼;不幸的是,這兩種類型都是用類型變量表示的,這使得它更難以看清。第一行表示你給出了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被重命名爲n1width :: (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 nHasArea的一個實例,這將需要width的類型爲(Num n, Num n1) => Room n -> n1。這是無法統一具體的n與一般n1這是導致您的類型錯誤。

有幾種方法可以解決這個問題。一種方法(也許是最好的方法),你可以在sepp2k's answer中看到的是使HasArea取類型變量* -> *;這意味着,而不是a本身是一種類型,例如a Inta n是類型。 Maybe[]是類型爲* -> *的示例。 (普通類型如IntMaybe 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 Intinstance 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)的類型,如果你能準確地找出那些是哪種類型,可以幫助你找出錯誤所在。

+0

哇,你真把那走出公園。我在Google上搜索這些確切的主題,因爲我立即發現缺乏將它用於多種類型的功能。不僅如此,我錯過了迄今爲止最簡單的多參數類型類。謝謝。 – 2011-01-11 02:33:07

9
class HasArea a where 
    width :: (Num n) => a -> n 

類型(Num n) => a -> n意味着對於任何類型n這是Num一個實例,width必須能夠返回該類型的值。因此,對於任何價值Tv,其中THasArea實例下面的代碼必須是有效的:

let x :: Integer = width v 
    y :: Double = width v 
in 
    whatever 

然而,這是不是你Room實例的情況。例如對於Room Integery :: Dobule = width v無效。

爲了使您的工作,例如,你可以做這樣的事情:

data Room n = Room n n deriving Show 

class HasArea a where 
    width :: (Num n) => a n -> n 

instance HasArea Room where 
    width (Room w h) = w 

在這裏我們不說Room IntegerRoom Float等都是HasArea實例,而是RoomHasArea一個實例。和width類型是這樣的,給定a n類型的值時,它產生n類型的值,所以如果你把一個Room Integer,你回來了Integer。這種方式適合。

+0

我明白了,謝謝你。這就說得通了。 – 2011-01-11 00:59:25