3

我有一些类型

data Foo = Foo
data Bar = Bar 
data Baz = Baz

我想将它们用作地图的键。这可能吗?如果可以,怎么做?

下面的附加上下文:

我有一个构建虚拟机的应用程序。我已将工作分为几个阶段。目前我有这种类型

data CurrentPhase = PHASEONE
                  | PHASETWO
                  | PHASETHREE (deriving Eq,Ord)

到目前为止一切顺利,没有像我上面提到的那样的问题。但是,我创建了一个类型类来描述特定于阶段的操作

class PhaseOps phase where
  preValidate :: JobID -> phase -> Handler (Status)
  doPreProc :: JobID -> phase -> Handler (Status)
  updateConfig :: JobID -> phase -> Handler ()
  postValidate :: JobID -> phase -> Handler (Status)

为了使其工作,我必须创建一组新的单例数据类型以用于PhaseOps实例。

data PhaseOne = PhaseOne

.. 等等

现在我有了这些单例类型,并且CurrentPhase. 我想摆脱CurrentPhase(我将它用于CurrentPhase作为键的 Map ),并使用我的单例数据类型。

4

1 回答 1

4

直接的解决方案是使用 type 的键Either Foo (Either Bar Baz)。当您添加可能的类型时,这会很快变得冗长,并且无论如何有点难看,因此使用特殊用途的等价物通常更有意义,例如:

data FooBarBaz = FooVal Foo | BarVal Bar | BazVal Baz

这类似于将它们直接组合成一种类型,但在组合类型中牺牲了更多的冗长性,以便能够在其他地方仍然使用单个类型。这是一种比较常见的模式;例如,我经常在表示语法树的类型中看到它,其中“顶级声明”类型可能采用这种形式,每种声明都是其自己的单独类型。

根据您的问题的性质,可能还有其他更好的方法,但以上是我能想到的唯一好的通用解决方案——如果您不喜欢这样做,您需要更清楚地说明原因并详细说明您需要这些类型来完成什么。


编辑以回应澄清:

正如我在对该问题的评论中提到的那样,PhaseOps它看起来很像一个想要成为函数记录的类。此外,如果你有这样一个类,想要一种方法来处理多个实例类型,就好像它们是单一类型一样,这强烈表明是时候退后一步重新考虑你的设计了。

继续这样的设计几乎总是会导致要么Typeable弄乱 ,正如 Thomas M. DuBuisson 在评论中提到的那样,要么弄乱存在类型(这是当今众所周知的反模式)。确实偶尔需要这种方法,但最好避免使用,除非您可以非常清楚地解释(即使只是对自己)为什么需要它们。否则,他们制造的问题远远多于他们解决的问题。

顺便说一句,如果您想保留单独类型的一些好处,我会考虑使用您的单例类型进行幻像类型标记和/或隐藏PhaseOps记录的构造函数并使用带CurrentPhase参数的智能构造函数。

于 2012-09-10T19:04:01.600 回答