2012-02-05 40 views
5

我想用haskell來實現一個遊戲,並且想使用一個類型類的系統來實現這個item系統。它的工作是這樣的:Haskell Typeclass檢查

data Wood = Wood Int 

instance Item Wood where 
    image a = "wood.png" 
    displayName a = "Wood" 

instance Flammable Wood where 
    burn (Wood health) | health' <= 0 = Ash 
        | otherwise = Wood health' 
     where health' = health - 100 

在項目和易燃類是這樣的:

class Item a where 
    image :: a -> String 
    displayName :: a -> String 

class Flammable a where 
    burn :: (Item b) => a -> b 

要做到這一點,我需要一種方法來檢測值是否是一個實例一個類型類。

Data.Data模塊提供了類似的功能,這使我相信這是可能的。

+2

我不確定你在做什麼符合Haskell類型模型。值是一個類型的實例應該是靜態可證明的。 – millimoose 2012-02-05 16:13:36

+4

值不能是類型類的實例。類型是類型類的實例。 – 2012-02-05 16:23:37

+4

請參見[本FAQ文章](http://www.haskell.org/haskellwiki/FAQ#I.27m_making_an_RPG._Should_I_define_a_type_for_each_kind_of_monster.2C_and_a_type_class_for_them.3F)。 – ehird 2012-02-05 16:35:47

回答

8

類型類可能是錯誤的方式去這裏。考慮使用普通的代數數據類型來代替,例如:

data Item = Wood Int | Ash 

image Wood = "wood.png" 
image Ash = ... 

displayName Wood = "Wood" 
displayName Ash = "Ash" 

burn :: Item -> Maybe Item 
burn (Wood health) | health' <= 0 = Just Ash 
        | otherwise = Just (Wood health') 
    where health' = health - 100 
burn _ = Nothing -- Not flammable 

如果這樣做太難添加新的項目,你可以代替編碼在數據類型本身的操作。

data Item = Item { image :: String, displayName :: String, burn :: Maybe Item } 

ash :: Item 
ash = Item { image = "...", displayName = "Ash", burn :: Nothing } 

wood :: Int -> Item 
wood health = Item { image = "wood.png", displayName = "Wood", burn = Just burned } 
    where burned | health' <= 0 = ash 
       | otherwise = wood health' 
      health' = health - 100 

但是,這會增加新功能的難度。同時進行這兩個操作的問題被稱爲expression problem。有一個nice lecture on Channel 9 by Dr. Ralf Lämmel他更深入地解釋這個問題,並討論各種非解決方案,如果你有時間,值得看看。

有些方法可以解決這個問題,但它們比我說過的兩種設計複雜得多,所以我建議使用其中一種,如果它適合您的需求,並且不必擔心表達問題,除非必須。

+0

第一個可能工作(雖然我不認爲它會沒有Data.Data黑客攻擊),但它會使添加更多項目(並且有超過200個)變得更加困難。 第二個根本不會工作,因爲有些物品不易燃。我的意思是說你可能有易燃的「伍德」牌子,可以放入庫存,並且可以打架的「殭屍」。 這兩個項目有一個共同的特點,但每個都有另一個特性,而另一個沒有。 – adrusi 2012-02-06 01:27:25

+0

這就是爲什麼我想要使用類型類,這是我必須能夠測試的障礙,例如,玩家與交互的物品是否易燃,這意味着測試是否是類型類的成員。 – adrusi 2012-02-06 01:28:26

+0

@adrusi:某個類是否是某個類的成員是一個編譯時屬性,因此它不適合你正在嘗試做的事情。從你所描述的內容來看,這聽起來像是第二種方法應該起作用,儘管你可能需要一些修改。這取決於某些項目之間的共同屬性。例如,如果任何物品可以具有任何屬性組合,則可以爲每個屬性設置一個'Maybe'字段,就像我爲易燃物品設置的那個字段。沒有那個屬性的項目在那裏只有一個'Nothing'。 – hammar 2012-02-06 01:54:48

5

這裏的問題:

burn :: (Item b) => a -> b 

這意味着是,burn結果值必須是多態性。它必須能夠填寫的任何洞的的任何Item的實例。現在

,它就是你想要寫這樣的事情(在假想的面向對象的語言與接口和子類)相當明顯:

Interface Item { 
    String getImage(); 
    String getDisplayName(); 
} 

Interface Flammable { 
    Item burn(); 
} 

在這種代碼,你說burn會產生一些項目,沒有任何保證約什麼樣的項目它是。這是「所有人」和「存在」之間的區別。你想在Haskell代碼中表達的是「存在」,但你實際上表達的是「爲所有人」。現在,如果你真的肯定你想要做

「存在」的功能,你可以看看使用Existential Types。但要小心。如果你打算編寫這樣的代碼:

if (foo instanceof Flammable) { 
    ... 
} 

然後你幾乎肯定是做錯了,會遇到很多痛苦和痛苦。相反,考慮哈馬爾建議的替代方案。