如何在Haskell中建模分层数据类型? [英] How to model hierarchical data types in Haskell?

查看:110
本文介绍了如何在Haskell中建模分层数据类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堆类型,他们的层次结构存储一些有用的信息。我试图避免将知识层次的知识烘焙到对它们进行操作的功能中。



以下是斯坦福大学自然语言处理类型化依赖项的一小段摘录:

  root  -  root 
dep - 依赖
aux - 辅助
auxpass - 被动辅助
cop - copula
arg - 参数
代理 - 代理

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



Haskell可能吗?我一直在试图声明各种 data 类型来对它进行建模,但是由于数据类型不能使用其他数据类型的构造函数,所以我无法使它工作。 / p>

我怀疑这种方法对于Haskell来说可能不太合适,所以如果是这种情况,你通常如何处理这些扁平化层次结构肯定不受欢迎的情况?

解决方案

一般来说,子类型并不能很好地与Haskell搭配。但是,如果您只是试图建模(非多重)继承(因此您有一个子类型树而不是格),则实际上可以使用类型类构建子类型。 这是一个简单的要点,可以做到这一点从那里开始工作,你定义你的数据类型

pre $ 数据Root =根...
数据Dep =从属...
数据Aux =辅助...
数据AuxPass =被动辅助...
数据Cop = Copula ...
数据Arg =参数...
数据Agent = Agent。 ..

以及相应的实例

 












$ =辅助
embedImmediate = ...

实例子类型Cop其中
类型SuperType Cop =辅助
embedImmediate = ...

实例子类型Arg其中
类型SuperType Arg = Dep
embedImmediate = ...

实例子类型代理程序其中
类型SuperType Agen t = Arg
embedImmediate = ...

如何填写 ... 取决于你。你可以为此做一些事情:如果你的子类型在超类型的顶部添加了很多字段,那么一直添加到这个字段中:


  • 如果您的子类型只添加了几个字段,则字段具有超类型并使 embedImmediate 返回该字段

  • 手动解开它们。您的数据定义看起来更整齐一些,但是您的 embedImmediate 定义会稍微长一点

  • 如果您的子类型不向超类型添加任何字段,那么您可以围绕超类型创建 newtype ,并且 embedImmediate = coerce (来自 Data.Coerce



你不能在 中仅在需要超类型的函数中使用子类型,但几乎:只需添加对嵌入的调用(与 embedImmediate !)将子类型转换为需要的超类型(基于类型推断)。您可能想查看一些示例用法



请注意,您现在可以使用<:作为约束条件:属于<$例如,c $ c> Aux 是(a< ;:Aux)=>一个。每当你想把这个东西当作一个 Aux (或者一个超类型的 Aux ),调用嵌入就可以了。



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


I have a bunch of types where their hierarchy stores some useful information. I'm trying to avoid having to bake in knowledge of the hierarchy of the types into the functions that operate on them.

The following is a little excerpt of Stanford's Typed Dependencies for natural language processing:

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

I would like to create some data types that mirror this structure so that I can define some functions that can only operate on certain types. When I have a function that operates on an arg, the type that I use to represent arg should also include agent but the type for agent should not include arg. The type for dep should include anything below it.

Is this possible in haskell? I've been trying to declare various data types to model this, but I can't get it to work since a data type cannot use the constructor of another data type.

I suspect that maybe this approach doesn't work very well with Haskell, so if that's the case how do you usually deal with these cases where flattening the hierarchy is definitely undesirable?

解决方案

Subtyping in general does not play too well with Haskell. However, in the case that you are just trying to model (non-multiple) inheritance (so you have a tree of subtypes instead of a lattice), you can actually build sub typing up using type classes. Here is a short gist that does exactly this. Working from there, you define your data types

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

And the corresponding instances

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 = ...

How you fill in the ... is up to you. A couple things you could do for this:

  • if your subtype adds lots of fields on top of the supertype, consistently, just add to it a field that has the supertype and make embedImmediate return that field
  • if your subtype adds only a couple of fields, you might want to manually unpack them. Your data definitions will look neater, but your embedImmediate definition will be a bit longer
  • if your subtypes doesn't add any fields to the supertype, you can just make a newtype around the super type and embedImmediate = coerce (from Data.Coerce)

Then, you can't quite just use the subtypes in functions that expect supertypes, but almost: you just have to add a call to embed (different from embedImmediate!) to convert from the subtype to whichever supertype is needed (based on type inference). You may want to check out some example uses.

Note that you can now use <: as a constraint: the type of something that is a subtype of Aux, for example, is (a <: Aux) => a. Whenever you want this thing treated as an Aux (or a super type of Aux), call embed on it.

The one big downside of this approach is that the input and output types must be annotated (else it isn't clear which supertype you want to embed into and you'll get "ambiguous type" errors). If you already write lots of signatures, you should be fine though.

这篇关于如何在Haskell中建模分层数据类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆