2011-12-02 128 views
5

我想分級定義的數據類型,如:分層數據類型

data Cat = BigCat | SmallCat 
data Animal = Cat | Dog 

,然後編寫,將採取的動物作爲自變量的函數,並將其與模式匹配這樣寫:

bigger::Animal -> Animal -> Bool 
bigger SmallCat BigCat = False 
bigger BigCat SmallCat = True 
bigger Dog Cat = True 
bigger Cat Dog = False 

編譯器抱怨。它不希望在功能簽名中明確寫入類型Animal與第一行和第二行模式匹配中的類型Cat相匹配。爲什麼Haskell不會承認一隻大貓或者小貓的一種動物?

+7

「爲什麼Haskell不承認一隻大貓或小貓是動物?」 - 因爲它不是。 'BigCat :: Cat'和'SmallCat :: Cat',但是'Cat :: Animal'。 *構造函數*'Cat'與* type *'Cat'沒有關係。 – delnan

回答

18

你混淆了類型與他們的構造函數。一個類型是你可以創建變量的東西。一個類型構造函數是你用來創建這樣的數據。在您的代碼中,data Animal = Cat | Dog聲明瞭類型Animal與兩個構造函數CatDog。在另一行中,您定義了一個數據類型Cat。這是沒有問題的,因爲類型和構造函數不共享相同的名稱空間。

如果你想擁有嵌入Cat類型的對象你Animal(如果構造Cat時),你可以添加一個字段的構造器:

data Animal = Cat Cat | Dog 

這意味着:Animal是具有兩個構造函數的類型CatDogCat有一個類型爲Cat的字段,而Dog沒有。如果你想用構造Cat創建對象,你必須Cat類型的對象傳遞給它:

myCat = Cat BigCat 

如果你想匹配的Animal,你必須列出匹配的構造函數的所有領域。比較你的代碼的修正版本:

data Cat = BigCat | SmallCat 
data Animal = Cat Cat | Dog 

bigger :: Animal -> Animal -> Bool 
bigger (Cat SmallCat) (Cat BigCat) = False 
bigger (Cat BigCat) (Cat SmallCat) = True 
bigger Dog   (Cat _)  = True 
bigger (Cat _)   Dog   = False 

_表示不在乎 - 不管傳遞的對象,這將始終一致。

+5

+1解釋適合初學者的類型和構造函數之間的區別。 – luqui

12

這裏的即時錯誤是Animal是定義兩個數據構造,裏面什麼都沒有做Cat:表達式CatAnimal型的,而表達BigCatCat型。

要在簡約時尚的嵌套的數據類型,你需要做Cat類型參數傳遞給相關的構造函數:

data Cat = BigCat | SmallCat 
data Animal = Cat Cat | Dog 

然後你可以做這樣的事情:

bigger (Cat SmallCat) (Cat BigCat) = False 
bigger (Cat BigCat) (Cat SmallCat) = True 
bigger Dog (Cat _) = True 
bigger (Cat _) Dog = False 

如果擴展超出一個簡單的例子,這變得非常笨拙,但Cat類型的冗餘是痛苦的,並且標識符Cat的兩種不同用途是不必要的混淆。略有改善是避免與物種混爲一談大小,而是做這樣的事情:

data Size = Big | Small 
data Species = Cat | Dog 
data Animal = Animal Species Size 

這裏的一個好處是,你可以更輕鬆地擴展任何類型而無需添加儘可能多的樣板廢話作爲原本所需。

然而,這些都是非常愚蠢的東西,除了玩具的例子,在實際使用中,很可能是一個更好的方法,這將是可取的。如果類型確實是簡單枚舉比貓和狗更有意義,那麼derivingOrdEnum,& c。比特殊用途更受歡迎。如果意圖是對具有各種屬性的實體進行建模的更開放的方式,那麼值得考慮針對實際問題更適合的其他設計。