F# 循环类型依赖

问题描述 投票:0回答:1

我正在使用 F# 创建一个小纸牌游戏,但由于循环类型依赖性,我遇到了一些问题。我有以下卡片类型(简化):

type Monster =
    { Health: int
      Attack: int
      SkillText: string option }

type Spell =
    { EffectText: string }

type Kind =
    | Monster of Monster
    | Spell of Spell

type Card =
    { Name: string
      Image: string
      Kind: Kind
      IsPlayer: bool
      CanPlay: Board -> bool }

这个想法是,在创建新卡牌时,必须根据当前棋盘状态定义其发挥所需的条件。

板型为

and Board =
    { PlayerHand: Hand
      EnemyHand: Hand
      PlayerDeck: Deck
      EnemyDeck: Deck
      PlayerField: Field
      EnemyField: Field
      PlayerGraveyard: Graveyard
      EnemyGraveyard: Graveyard }

我的问题是,Board内部的类型都依赖于

Card
类型,所以我需要在Card类型之后定义它,但是Card类型取决于Board类型。我知道我可以使用泛型来撤销循环依赖,但我有以下问题:

如果我定义一个

Board<'Card>
类型,我需要将 Board 引用的所有类型设置为通用类型。这不仅对我来说显得很冗长,而且对我的应用程序也没有多大意义。我永远不会做任何不是牌的东西。我知道我可以改为
Card<'Board>
,但这似乎不太直观,而且我在
Board<'Card>
中遇到的问题同样适用。

有没有什么方法可以在不使用泛型的情况下解决这个问题,同时保持纯粹的功能(除了对依赖于

and
的每种类型使用
Card
关键字)?

f# circular-dependency
1个回答
0
投票

是的!有很多方法可以解决这个问题

1.
and
关键字

我看到你已经在

and Board = ...
中使用过它,所以我假设你知道它,但你不想使用它。以防万一:

type Card =
    { Name: string
      Image: string
      Kind: Kind
      IsPlayer: bool
      CanPlay: Board -> bool }

and Board =
    { PlayerHand: Hand
      EnemyHand: Hand
      PlayerDeck: Deck
      EnemyDeck: Deck
      PlayerField: Field
      EnemyField: Field
      PlayerGraveyard: Graveyard
      EnemyGraveyard: Graveyard }

这允许您一起编写循环类型,但它们必须位于同一个文件中。

2.打破你的依赖

这取决于您想要的游戏,但也许 CanPlay 不需要依赖于棋盘?通常这样的事情可以是一个简单的枚举:

type PlayCondition =
    | BoardIsEmpty
    | ManaIsAbove of int

type Card =
    { Name: string
      Image: string
      Kind: Kind
      IsPlayer: bool
      CanPlay: PlayCondition }

3.使用界面

使用您想要在板上使用的功能定义您的界面:

type IBoard =
    abstract member GetCardAt: x: int -> y: int -> Card

and Card =
    { Name: string
      Image: string
      Kind: Kind
      IsPlayer: bool
      CanPlay: IBoard -> bool }

在船上实施:

type Board =
    { PlayerHand: Hand
      EnemyHand: Hand
      PlayerDeck: Deck
      EnemyDeck: Deck
      PlayerField: Field
      EnemyField: Field
      PlayerGraveyard: Graveyard
      EnemyGraveyard: Graveyard }
    interface IBoard with
        member this.GetCardAt x y = ...

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.