Haskell中动态构造的枚举类型

时间:2015-09-03 09:09:30

标签: haskell dynamic types configuration config

假设我正在设计一个游戏引擎,其中可能的操作存储为使用构造函数实现的枚举类型。例如:

data Action = Walk | Attack | Drop | PickUp | DoMagic

这很简单,可以使用此作为基本构建块构建游戏引擎。但是,设计人员希望使用以下类型的配置文件设置可能的操作:

[Actions]
    Walk
    Attack
    Drop
    PickUp
    DoMagic
[/Actions]

如何将这样的文件转换为Action类型?换句话说,我如何基于配置文件动态构造类型?

1 个答案:

答案 0 :(得分:1)

这听起来像是一个如何定义抽象数据类型的简单问题。对于模块级抽象,您可以使用Haskell的机制来选择要导出的内容;对于包级抽象,您可以使用Haskell的机制来选择要公开的模块。我将在下面简要讨论每一个。

对于模块级抽象,你会写这样的东西:

module Action (Action, act, allActions) where

import Environment
import Actor

data Action = Walk | Inspect

-- give some way for other modules to construct actions
allActions :: [Action]
allActions = [Walk, Inspect]

-- give some way for other modules to consume actions
act :: Actor -> Action -> Environment -> Environment
act = undefined -- exercise for the reader

这将是您的配置文件"。 (我认为你提出的配置文件 - 列出了操作,但没有说出它们的意思 - 是不合理的最小化。)你的库的其他部分 - 也就是说,在这个模块边界之外 - 将被迫是Action - 不可知的,因为他们不能以有意义的方式依赖于可用的特定行动列表。特别是,他们无法在Action上进行模式匹配,也无法在Action选择的某些操作集合上创建任意act s - allActions。因此,只要您维护allActionsact界面,就可以以任何方式更改配置。

当然,在实际项目中,在单个模块中具有所有Action特定操作可能有点笨拙。因此,当事情变得更大时,使用包级抽象可能更好。 Haskell社区中的一个常见模式是具有导出所有内容的Internal模块,以及仅导出消费者应该可用的位的普通模块:

module Action.Internal (Action(..)) where

data Action = Walk | Inspect

module Action (Action, allActions, act) where

import Action.Internal
import Environment
import Actor

allActions = [Walk, Inspect]
act = undefined

在您的软件包的cabal文件中,可以将Internal模块隐藏在其他软件包中:

library
    exposed-modules: Action
                     Actor
                     Environment
    other-modules:   Action.Internal

但是,现在以Python的方式实现这一点变得越来越普遍:公开所有模块,理解触摸Internal模块的消费者是同意的成年人(特别是了解他们可能需要处理内部不变量,API可能无法预测地改变等。)。