我有一堆类型,其层次结构存储一些有用的信息。我试图避免不得不将类型层次结构的知识烘焙到对其进行操作的函数中。
以下是斯坦福大学自然语言处理的类型依赖关系摘录:
root - root
dep - dependent
aux - auxiliary
auxpass - passive auxiliary
cop - copula
arg - argument
agent - agent
我想创建一些镜像此结构的数据类型,以便我可以定义一些只能在某些类型上运行的函数。当我有一个在arg
上运行的函数时,我用来表示arg
的类型也应包含agent
,但agent
的类型不应包含arg
}}。 dep
的类型应包含其下方的任何内容。
这是否可以在haskell?我一直在尝试声明各种data
类型来对此进行建模,但由于数据类型无法使用其他数据类型的构造函数,因此我无法使其工作。
我怀疑这种方法可能与Haskell不能很好地协作,所以如果是这样的话,你通常如何处理这些平铺层次结构绝对不可取的情况呢?
答案 0 :(得分:1)
一般来说,对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
返回该字段data
定义看起来更整洁,但embedImmediate
定义会更长一些newtype
周围embedImmediate = coerce
(来自Data.Coerce
)然后,您可以完全只使用期望超类型的函数中的子类型,但几乎:您只需要添加对embed
的调用(与{{不同) 1}}!)从子类型转换为需要的超类型(基于类型推断)。您可以查看some example uses。
请注意,您现在可以使用embedImmediate
作为约束:例如<:
子类型的内容类型为Aux
。每当您希望将此内容视为(a <: Aux) => a
(或Aux
的超级类型)时,请在其上调用Aux
。
这种方法的一个重大缺点是输入和输出类型必须注释(否则它不是 哪个超类型你希望embed
进入你&#&# 39; ll得到&#34;模糊类型&#34;错误)。如果你已经写了很多签名,你应该没事。
答案 1 :(得分:0)
使用类型类。
首先将每个具体类型定义为单独的数据声明。
然后,对于每个具有子类型的类型,声明一个在其父类型上具有约束的类型类。这种关系的一个例子是Functor =&gt; Applicative =&gt; Monad结构在前奏中。
所以定义示例结构:
class Root where
...
class Root => Dep where
...
class Dep => Aux where
...
class Aux => Auxpass where
...
class Aux => Cop where
...
class Dep => Arg where
...
class Arg => Agent where
...