我有一些类型
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
作为键的地图),并使用我的单例数据类型。
答案 0 :(得分:4)
直接的解决方案是使用Either Foo (Either Bar Baz)
类型的键。当你添加可能的类型时,这会很快变得冗长,并且有点难看,因此使用特殊用途的等效项通常更有意义,例如:
data FooBarBaz = FooVal Foo | BarVal Bar | BazVal Baz
这类似于将它们直接组合成一种类型,但在组合类型中更加冗长,以便仍能在其他地方使用各种类型。这是一种相对常见的模式;例如,我经常在表示语法树的类型中看到它,其中“顶级声明”类型可能采用这种形式,每种声明都是它自己的独立类型。
根据问题的性质,可能会有更好的其他方法,但上面是我能想到的唯一一个很好的通用解决方案 - 如果你不喜欢这样做,你'我需要更清楚地指定为什么并详细说明你需要这些类型来完成什么。
编辑以回应澄清:
正如我在关于这个问题的评论中所提到的,PhaseOps
看起来很像一个想要成为功能记录的类。此外,如果你有这样一个类,想要一种方法来处理多个实例类型,就像它们是单一类型一样,这是一个非常强烈的迹象,表明它是时候退后一步并重新思考你的设计了。
继续这样的设计几乎总是导致与Typeable
混在一起,正如Thomas M. DuBuisson在评论中提到的那样,或者与存在主义类型(这是众所周知的反模式)相混淆天)。确实,这些方法偶尔需要,但最好避免使用,除非您能够非常清楚地解释(即使只是为了您自己)为什么需要它们。否则,他们会产生比他们解决的问题更多的问题。
顺便提一下,如果你想保留单独类型的一些好处,我会考虑使用你的单例类型进行幻像类型标记和/或隐藏PhaseOps
记录的构造函数,并使用智能构造函数采用CurrentPhase
参数。