如何在Haskell中建模分层数据类型? [英] How to model hierarchical data types in 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 yourembedImmediate
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 andembedImmediate = coerce
(fromData.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屋!