尽管标题我不会问OO世界和Haskell之间的翻译,但我无法找到更好的标题。这个讨论与this one类似,但并不相同。
我开始了一个玩具项目,只是为了扩展我对Haskell的有限知识,同时阅读“为了一个伟大的好事学习你的哈斯克尔”,我决定实施一个非常基本的“元素类型系统”,这是一个Final Fantasy et simila等游戏中典型战斗系统的子集。 我正在跳过大部分细节,但这简直就是我的问题:
我想模拟一个咒语,一个可以施放在玩家或怪物身上的魔法。在OO世界中,你通常使用“onCast(播放器)”方法进行“可投射”界面,这是一个“拼写”类,所以你可以定义这样的东西
Spell myNewSpell = Spell("Fire", 100, 20);
myNewSpell.onCast(Player p); //models the behaviour for the Fire spell
在Haskell中,我根据类型和类来考虑这一点(我知道Haskell中的类是一个不同的概念!)。我遇到了一些困难,因为我的第一次尝试是创造这个:
--A type synonim, a tuple (HP,MP)
type CastResult = (Integer,Integer)
--A castable spell can either deal damage (or restore) or
--inflict a status
class Castable s where
onCast :: s -> Either (Maybe Status) CastResult
data Spell = Spell{spellName :: String,
spellCost :: Integer,
spellHpDmg :: Integer,
spellMpDmg :: Integer,
spellElem :: Maybe Element} deriving (Eq,Show,Read)
现在假设我使用记录语法
创建一些法术bio = Spell{spellName = "Bio", ...etc..}
我希望能够做到这样的事情
instance Castable bio where
onCast bio = Left (Just Poison)
这里有很多问题:
1)我不能做“Castable bio”,因为bio必须是具体的类型,而不是Type的值(它应该是Castable Spell)
2)bio不在范围内,实例块内部被视为与
模式匹配的值总的来说,我觉得这种设计选择很差,但我还在学习,而且我没有掌握像Functors这样的高级主题,只是为了命名。
简而言之,处理这种情况的惯用方法是什么?我的意思是需要“一个定义,多个实例的多个实现”的情况,只是为了使用OO术语。
谢谢大家,快乐编码,
阿尔弗雷
答案 0 :(得分:14)
当您处理不同的类型时,类型类很有用。但是,在这种情况下,我似乎在处理单独的实例。在这种情况下,将cast函数只是另一个记录字段可能是最简单的。
data Spell = Spell{spellName :: String,
...
onCast :: Either (Maybe Status) CastResult }
deriving (Eq,Show,Read)
bio = Spell { spellName = "Bio", onCast = Left (Just Poison), ... }
或者您可以使用特定于域的类型而不是像Either
这样的通用类型来更明确地模拟您的需求。
type ManaPoints = Integer
type HitPoints = Integer
data Spell = Spell { spellName :: String,
spellCost :: ManaPoints,
spellElem :: Maybe Element,
spellEffect :: Effect }
data Effect = Damage HitPoints ManaPoints
| Inflict Status
cast :: Spell -> Player -> Player
cast spell player =
case spellEffect spell of
Damage hp mana = ...
Inflict status = ...
bio = Spell { spellName = "Bio", spellEffect = Inflict Poison, ... }
fire = Spell { spellName = "Fire", spellEffect = Damage 100 0, ... }
答案 1 :(得分:3)
data Spell = Spell{ spellName :: String
, spellCost :: Integer
, spellHpDmg :: Integer
, spellMpDmg :: Integer
, spellElem :: Maybe Element
, spellStatus :: Maybe Status
}
deriving (Eq,Show,Read)
class Castable s where
onCast :: s -> (CastResult, Maybe Status)
instance Castable Spell where
onCast s = ((spellHpDmg s, spellMgDmg s), spellStatus s)
这可能会解决这个问题,但不确定这个类在这种情况下是否有用。除了法术施法者之外还有别的东西吗?像技能或物品?
答案 2 :(得分:3)
如果我理解正确,我认为你应该onCast
Spell
的另一个记录字段,然后你可以写:
bio = Spell{spellName = "Bio", ...etc.., onCast = Left (Just Poison)}
您将无法再执行编辑:实际上onCast不是函数类型,所以请忽略它。deriving (Eq,Show,Read)
,因为Spell
现在包含一个函数类型。您必须手动编写这些实例。