haskell - 递归类型是haskell,它不重复相同的构造函数

标签 haskell recursion types tree gadt

我想在 Haskell 中用不同的节点标签制作一棵树。一般情况如下所示:

data Tree a
    = Leaf a
    | Node1 [Tree a]
    | Node2 [Tree a]
    ...
    | NodeN [Tree a]

现在我想告诉编译器每个特定的节点都不会重复。我使用 GADT 和总和类型实现了这一点

data Tree t a where
  Leaf  :: a -> Tree t a
  Node1 :: [Either (Tree 2 a) (Tree 3 a)] -> Tree 1 a
  Node2 :: [Either (Tree 1 a) (Tree 3 a)] -> Tree 2 a
  Node3 :: [Either (Tree 1 a) (Tree 2 a)] -> Tree 3 a

但出于多种原因,我不喜欢我的解决方案。

  • 实例没有明确定义,因为您可以将 Leaf 放入 LeftRight 中。
  • 添加另一个节点会更改所有其他构造函数的类型。
  • 从外部使用这棵树会带来痛苦,因为特定树的类型取决于其根。

有更好的解决办法吗?我可以说“我想要每种类型 Tree t a 但不是 Tree 1 a”吗?

更新 1 根据 chi 的回答我做了这个

data Tag = L | A | B | C

data Tree (a :: Tag) v where
    Leaf :: l -> Tree L v
    N1 :: NotEqual a A => [Tree a v] -> Tree A v
    N2 :: NotEqual a B => [Tree a v] -> Tree B v
    N3 :: NotEqual a C => [Tree a v] -> Tree C v

type family NotEqual (a :: Tag) (b :: Tag) :: Constraint where
  NotEqual x x = TypeError (Text "forbidden Case")
  NotEqual _ _ = ()

我工作得很好,但我无法用它创建 N1 [N2 [Leaf 1], N3 [Leaf 3] ]

最佳答案

您可能想在此处使用自定义约束,并利用 DataKinds

下面,我只展示一个未经测试的草图。

我们首先定义标签的类型。

data Tag = T1 | T2 | ...

然后我们用禁止标签列表来注释树,即那些不能在树中使用的标签。每次我们使用构造函数时都会检查此列表。

data Tree (forbidden :: [Tag]) a where
  Leaf  :: a -> Tree forbidden a
  Node1 :: NotIn 'T1 forbidden => [Tree ('T1 ': forbidden) a] -> Tree forbidden a
  Node2 :: NotIn 'T2 forbidden => [Tree ('T2 ': forbidden) a] -> Tree forbidden a
  ...

现在我们需要定义约束。我们可以通过“禁止标签列表”进行归纳。

type family NotIn (x :: Tag) (xs :: [Tag]) :: Constraint where
   NotIn _ '[]       = ()
   NotIn x '(x :  _) = TypeError "forbidden case"
   NotIn x '(_ : xs) = NotIn x xs

这种方法的主要缺点是我们无法编写 O(1) 弱化,如

weakening :: Tree '(t : f) a -> Tree f a

为此,我们需要使用 O(N) 时间遍历整个树。

关于haskell - 递归类型是haskell,它不重复相同的构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63102557/

相关文章:

haskell - 为什么 `($ 4) (> 3)` 等同于 `4 > 3` ?

c - hsc2hs:用 Haskell 改变 C 结构

Haskell:使用镜头修改状态时 TChan 包装重复

python - Python中列表的递归排序函数

generics - 参数化类型的特定生成器

c - C 中的类型不匹配

haskell - 带 fclabel 的 STM

javascript - 在对象内迭代数组并相应地添加属性

javascript - 哪种风格更适合 checkin JavaScript?

c++ - 使用递归函数加密用户输入