简体   繁体   English

Haskell中动态构造的枚举类型

[英]Dynamically-constructed enumerated types in Haskell

Say I were designing a game engine in which possible actions are stored as an enumerated type implemented with constructors. 说我正在设计一个游戏引擎,其中可能的动作存储为通过构造函数实现的枚举类型。 For example: 例如:

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

This is simple enough, a game engine can be built using this as a fundamental building block. 这很简单,可以以此为基础构建游戏引擎。 But say the designers wanted to set possible actions using a configuration file, of the following type: 但是说设计者想使用以下类型的配置文件来设置可能的动作:

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

How does one turn a file like this into the Action type? 如何将这样的文件转换为Action类型? In other words, how would I dynamically construct types based on config files? 换句话说,我将如何基于配置文件动态构造类型?

This sounds like a simple question of how to define an abstract data type. 这听起来像一个关于如何定义抽象数据类型的简单问题。 For module-level abstraction, you can use Haskell's mechanism for choosing what to export; 对于模块级别的抽象,您可以使用Haskell的机制来选择要导出的内容。 for package-level abstraction, you can use Haskell's mechanism for choosing which modules to expose. 对于包级别的抽象,可以使用Haskell的机制来选择公开哪些模块。 I'll briefly discuss each below. 我将在下面简要讨论每个。

For module-level abstraction, you would write something like this: 对于模块级抽象,您将编写如下内容:

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

This would be your "configuration file". 这将是您的“配置文件”。 (I think your proposed configuration file -- which lists actions, but doesn't say what they mean -- is unreasonably minimal.) Other parts of your library -- that is, outside this module boundary -- will be forced to be Action -agnostic in the sense that they cannot depend on the specific list of actions available in a meaningful way. (我认为您建议的配置文件(其中列出了动作,但未说明它们的意思)过分地小。)您库的其他部分(即在此模块边界之外)将被强制为Action -不可知论,即它们不能以有意义的方式依赖于可用的特定操作列表。 In particular, they will not be able to pattern match on Action s or create arbitrary Action s -- only act on some collection of actions chosen from allActions . 特别是,他们将无法在模式匹配Action S或创建任意Action的S -只act于从选择一些行动的集合allActions So you can change the configuration in any way you like provided you maintain the allActions and act interface. 因此,只要您维护allActionsact界面,就可以按照自己喜欢的任何方式更改配置。

Of course, in real projects, it can be a bit unwieldy to have all of the Action -specific operations in a single module. 当然,在实际项目中,将所有特定于Action操作放在一个模块中可能有点笨拙。 So when things get a bit bigger, it's probably better to use package-level abstraction. 因此,当事情变得更大时,使用包级抽象可能会更好。 One common pattern in the Haskell community is to have an Internal module that exports everything, and a normal module that only exports the bits that should be available to consumers: 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

In your package's cabal file, one can then hide the Internal module from other packages: 然后,可以在您的包裹的阴谋文件中,将Internal模块从其他包裹中隐藏:

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

These days, though, it is becoming much more common to do this the Python way: expose all the modules, with the understanding that consumers that touch the Internal modules are consenting adults (and in particular understand that they may need to take care of internal invariants, that the API could change unpredictably, etc.). 不过,如今,以Python方式进行操作变得越来越普遍:公开所有模块,并理解接触Internal模块的消费者已同意成年人(尤其是了解到他们可能需要照顾内部不变,API可能会发生不可预期的变化等)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM