【发布时间】:2018-05-16 20:33:44
【问题描述】:
考虑以下数据结构,表示一棵树,其层级不断增加,但不一定是连续的:
data MyTree (n :: T) where
MyLeaf :: MyTree n
MyNode :: Plus n m z => [MyTree ('Succ z)] -> MyTree n
其中T代表类型级别的Peano数,定义为
class Plus (n :: T) (m :: T) (r :: T) | r n -> m
instance Plus 'Zero m m
instance Plus n m r => Plus ('Succ n) m ('Succ r)
像这样构造树很容易
myTreeOne :: MyTree ('Succ 'Zero)
myTreeOne = MyNode ([ MyLeaf ] :: [MyTree ('Succ ('Succ 'Zero))])
myTree :: MyTree 'Zero
myTree = MyNode [ MyLeaf, myTreeOne ]
或
myLeafTwo :: MyTree ('Succ ('Succ 'Zero))
myLeafTwo = MyLeaf
myOtherTree :: MyTree 'Zero
myOtherTree = MyNode [ myLeafTwo ]
现在我想定义以下函数:
myTreeComponents MyLeaf = []
myTreeComponents (MyNode components) = components
它只是提取树的直接子节点列表,但我无法写出正确的类型。
这是我得到的错误
• Couldn't match expected type ‘p’ │
with actual type ‘[MyTree ('Succ z)]’ │
because type variable ‘z’ would escape its scope │
This (rigid, skolem) type variable is bound by │
a pattern with constructor: │
MyNode :: forall (n :: T) (m :: T) (z :: T). │
Plus n m z => │
[MyTree ('Succ z)] -> MyTree n, │
in an equation for ‘myTreeComponents’ │
at src/Model.hs:159:19-35 │
• In the expression: components │
In an equation for ‘myTreeComponents’: │
myTreeComponents (MyNode components) = components │
• Relevant bindings include │
components :: [MyTree ('Succ z)] (bound at src/Model.hs:159:26) │
myTreeComponents :: MyTree n -> p (bound at src/Model.hs:158:1) │
| │
159 | myTreeComponents (MyNode components) = components │
| ^^^^^^^^^^
对于依赖类型语言,它应该类似于
exists m. Plus n m z => MyTree n -> [ MyTree ('Succ z) ]
是否可以在 Haskell 中编写这样的类型?否则我应该如何编写我的函数?
【问题讨论】:
-
您需要使用自定义
dataGADT 定义自己的存在类型。它看起来像MyTree,但只有第二个构造函数。但是,从第一个类型中提取m,z以在存在主义中使用是非常棘手的:如果您可以将代理添加到MyNode构造函数,可能会更容易。 (这很痛苦。如果 Haskell 有真正的依赖类型,或者更好的模糊类型,那就更容易了。) -
感谢@chi,很抱歉,但我发现很难遵循您的建议。能否请您详细说明一下答案?
-
在写评论之前,我花了一些时间尝试解决方案,但花费的时间比我预期的要长,所以我中途停了下来。
-
@chi,添加代理应该不会太糟糕。如果它被标记为严格,它将“解包”为空。有一个 GHC 提案可以更好地绑定存在类型变量。
-
@chi,见the proposal。我个人有点担心所有这些类型变量的排序对未围绕这些功能设计的库 API 有何影响。