Haskell中类似OO的接口实现 [英] OO-Like interface implementation in Haskell
问题描述
我已经开始了一个玩具项目,只是为了扩展我对Haskell有限的知识,同时阅读学习你对一个伟大的好东西的Haskell,并且我已经决定实现一个非常基本的元素类型系统,它是诸如Final Fantasy et simila等游戏中典型战斗系统的子集。
我跳过了大部分细节,但这简单来说就是我的问题:
我想塑造一个咒语,一个可以施展的魔法玩家还是怪物。在OO世界中,您通常会使用onCast(Player)方法来创建一个Castable接口,这是一个Spell类,因此您可以定义像这样的事物。
拼写myNewSpell = Spell(Fire,100,20);
myNewSpell.onCast(Player p); //为Fire的行为建立模型
在Haskell中,我认为这是根据类型和类我知道Haskell中的类是一个不同的概念!)。我遇到了一些困难,因为我的第一次尝试是创建这个:
- 一个类型synonim,一个元组(HP ,MP)
类型CastResult =(整数,整数)
- 一个可施法咒语可以造成伤害(或恢复)或
- 触发一个状态
类Castable s其中
onCast :: s - >或者(可能状态)CastResult
数据Spell = Spell {spellName :: String,
spellCost :: Integer,
spellHpDmg :: Integer,
spellMpDmg :: Integer,
spellElem ::可能元素}派生(Eq,Show,Read)
<现在假设我使用记录语法来创建一些咒语
$ p code bio = Spell {spellName =Bio,... etc ..}
我希望能够做到这一点
instance Castable bio其中
onCast bio = Left(Just Poison)
这里有很多问题:
$ b $ 1)我不能做Castable生物,因为生物必须是具体类型,而不是类型的值(它应该是可施放的法术)b / b
2)生物不在范围内,在实例区块内被看作是一个模式值与
匹配总的来说,我感觉这个ch设计的风格很差,但我仍然在学习,而且我没有掌握像Functors这样的高级主题,只是为了举一个名字。
简而言之,这是处理这种情况的惯用方式吗?我的意思是这种情况需要一个定义,多个实例的多个实现,只是使用OO术语。 p>
Alfredo
类型。然而,在这种情况下,我觉得你好像在处理单独的实例。在这种情况下,将cast功能设置为另一个记录字段可能是最简单的。
data Spell = Spell {spellName: :字符串,
...
onCast :: Either(可能状态)CastResult}
派生(Eq,Show,Read)
bio = Spell {spellName = Bio,onCast = Left(Just Poison),...}
或者你可以做一些事情它可以更明确地模拟您的需求,使用特定于域的类型而不是像或者
这样的通用类型。
type ManaPoints =整数
类型HitPoints =整数
数据Spell = Spell {spellName :: String,
spellCost :: ManaPoints,
spellElem ::可能元素,
spellEffect ::效果}
数据效果=伤害HitPoints ManaPoints
| inflict状态
cast :: Spell - >玩家 - >玩家
施放法术玩家=
case spellEffect法术
伤害hp法力= ...
施放状态= ...
bio = Spell { spellName =Bio,spellEffect = Inflict Poison,...}
fire = Spell {spellName =Fire,spellEffect = Damage 100 0,...}
despite the title I'm not going to ask about a mere translation between OO world and Haskell, but I can't figure out a better title. This discussion is similar, but not equal, to this one.
I've started a toy project just to expand my limited knowledge of Haskell while reading "Learn You a Haskell for a Great Good", and I've decided to implement a very basic "Elemental Type System", which is a subset of a typical battle system in games like Final Fantasy et simila. I'm skipping most of the details, but this is in a nutshell my problem:
I want to model a spell, a magic you can cast on the player or on a monster. In the OO world you usually go for a "Castable" interface with a method "onCast(Player)", a "Spell" class so you can define thing like this
Spell myNewSpell = Spell("Fire", 100, 20);
myNewSpell.onCast(Player p); //models the behaviour for the Fire spell
In Haskell I thought this in terms of Types and Classes (I know that Classes in Haskell are a different concept!). I've encountered some difficulties, because my first attempt was to create this:
--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)
Now suppose I create some spell using the Record Syntax
bio = Spell{spellName = "Bio", ...etc..}
I would like be able to do something like this
instance Castable bio where
onCast bio = Left (Just Poison)
There are many problems here:
1) I can't do "Castable bio" since bio must be a concrete type, not a value of the Type (It should be Castable Spell)
2) bio isn't in scope, inside the instance block is seen just as a value to pattern match against
Overall, I feel this choice of design is pretty poor, but I'm still learning and I don't grasp such advanced topics like Functors, just to name one.
In a nutshell, which is the idiomatic way to dealing with situation like this? I mean situation wich requires "one definition, multiple implementation for multiple instances", just to use the OO terminology.
Thanks everyone, happy coding,
Alfredo
Type classes are useful when you're dealing with different types. In this case, however, it seems to me like you're dealing with separate instances. In such a case, it's probably simplest to have the cast function be just another record field.
data Spell = Spell{spellName :: String,
...
onCast :: Either (Maybe Status) CastResult }
deriving (Eq,Show,Read)
bio = Spell { spellName = "Bio", onCast = Left (Just Poison), ... }
Or you could do something that models your requirements more explicitly, using domain-specific types rather than generic ones like 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, ... }
这篇关于Haskell中类似OO的接口实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!