Haskell 中类似 OO 的接口实现

2024-02-24

尽管有这个标题,我不会仅仅询问 OO 世界和 Haskell 之间的翻译,但我想不出更好的标题。此讨论类似于但不等于this one https://stackoverflow.com/questions/5474171/oo-interface-translation-to-haskell.

我开始了一个玩具项目,只是为了在阅读“Learn You a Haskell for a Great Good”时扩展我对 Haskell 的有限知识,并且我决定实现一个非常基本的“元素类型系统”,它是《最终幻想》等游戏中的典型战斗系统。 我跳过了大部分细节,但这简而言之是我的问题:

我想模拟一个咒语,一种可以对玩家或怪物施放的魔法。在面向对象的世界中,您通常会使用带有“onCast(Player)”方法的“Castable”接口,一个“Spell”类,这样您就可以定义这样的东西

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 必须是具体类型,而不是类型的值(它应该是 Castable Spell)

  2. bio 不在范围内,在实例块内被视为模式匹配的值

总的来说,我觉得这种设计选择相当糟糕,但我仍在学习,我不掌握像函子这样的高级主题,仅举一例。

简而言之,处理这种情况的惯用方法是什么?我的意思是需要“一个定义,多个实例的多个实现”的情况,只是使用面向对象的术语。


当您处理不同的情况时,类型类非常有用types。然而,在这种情况下,在我看来,你正在处理单独的实例。在这种情况下,最简单的方法可能是让强制转换函数只是另一个记录字段。

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, ... }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Haskell 中类似 OO 的接口实现 的相关文章

随机推荐