Haskell地图与不同类型的键

时间:2012-09-10 18:27:53

标签: haskell types

我有一些类型

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

我想将它们用作Map的键。这是可能的,如果是的话,怎么样?

下面的附加背景:

我有一个构建VM的应用程序。我把工作分成了阶段。目前我有这种类型

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作为键的地图),并使用我的单例数据类型。

1 个答案:

答案 0 :(得分:4)

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

data FooBarBaz = FooVal Foo | BarVal Bar | BazVal Baz

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

根据问题的性质,可能会有更好的其他方法,但上面是我能想到的唯一一个很好的通用解决方案 - 如果你不喜欢这样做,你'我需要更清楚地指定为什么并详细说明你需要这些类型来完成什么。


编辑以回应澄清:

正如我在关于这个问题的评论中所提到的,PhaseOps看起来很像一个想要成为功能记录的类。此外,如果你有这样一个类,想要一种方法来处理多个实例类型,就像它们是单一类型一样,这是一个非常强烈的迹象,表明它是时候退后一步并重新思考你的设计了。

继续这样的设计几乎总是导致与Typeable混在一起,正如Thomas M. DuBuisson在评论中提到的那样,或者与存在主义类型(这是众所周知的反模式)相混淆天)。确实,这些方法偶尔需要,但最好避免使用,除非您能够非常清楚地解释(即使只是为了您自己)为什么需要它们。否则,他们会产生比他们解决的问题更多的问题。

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