如何在Haskell中建模分层数据类型?

我有一堆类型,他们的层次结构存储一些有用的信息.我试图避免不得不将类型层次结构的知识烘焙到对其进行操作的函数中.

以下是斯坦福自然语言处理的Typed Dependencies的一些摘录:

root - root
dep - dependent
  aux - auxiliary
    auxpass - passive auxiliary 
    cop - copula
  arg - argument 
    agent - agent

我想创建一些镜像这种结构的数据类型,以便我可以定义一些只能在某些类型上运行的函数.当我有一个对arg进行操作的函数时,我用来表示arg的类型也应该包含agent,但是agent的类型不应该包含arg. dep的类型应该包括它下面的任何东西.

这在哈斯克尔有可能吗?我一直在尝试声明各种数据类型来对此进行建模,但由于数据类型无法使用其他数据类型的构造函数,因此无法使其工作.

我怀疑这种方法可能与Haskell不能很好地协作,所以如果是这样的话,你通常如何处理这些情况,其中扁平化层次结构肯定是不可取的?

最佳答案 对于Haskell来说,一般来说,子类型不能很好地发挥作用.但是,如果您只是尝试建模(非多重)继承(因此您有一个子类型树而不是格子),您实际上可以使用类型类构建子类型.
Here is a short gist that does exactly this.从那里开始,您可以定义数据类型

data Root = Root ...
data Dep = Dependent ...
data Aux = Auxiliary ...
data AuxPass = PassiveAuxiliary ... 
data Cop = Copula ...
data Arg = Argument ...
data Agent = Agent ...

和相应的实例

instance Subtype Aux where
  type SuperType Aux = Dep
  embedImmediate = ...

instance Subtype AuxPass where
  type SuperType AuxPass = Aux
  embedImmediate = ...

instance Subtype Cop where
  type SuperType Cop = Aux
  embedImmediate = ...

instance Subtype Arg where
  type SuperType Arg = Dep
  embedImmediate = ...

instance Subtype Agent where
  type SuperType Agent = Arg
  embedImmediate = ...

你如何填写…取决于你.你可以为此做几件事:

>如果您的子类型在超类型之上添加了大量字段,则只需添加一个具有超类型的字段并使embedImmediate返回该字段
>如果您的子类型只添加了几个字段,您可能需要手动解压缩它们.您的数据定义看起来更整洁,但您的embedImmediate定义会更长一些
>如果您的子类型没有向超类型添加任何字段,您可以在超类型和embedImmediate = coerce(来自Data.Coerce)周围创建一个新类型

然后,您不能仅仅在期望超类型的函数中使用子类型,而是几乎:您只需要添加对嵌入的调用(与embedImmediate!不同)以从子类型转换为需要的任何超类型(基于类型推断) ).您可以查看some example uses.

请注意,您现在可以使用&lt ;:作为约束:例如,Aux的子类型的某种类型是(a&lt ;: Aux)=>一个.每当你想把这个东西当作Aux(或Aux的超级类型)对待时,就可以调用它.

这种方法的一大缺点是必须注释输入和输出类型(否则不清楚要嵌入哪种超类型,你会得到“模糊类型”错误).如果你已经写了很多签名,你应该没问题.

点赞