如何创建只接受其他字段的字段?

时间:2016-11-21 15:33:12

标签: idris

我想创建一个包含交替构造函数FooBar的数据类型。例如,有效成员将是:

Foo (Bar (Foo (Bar End)))

但不是:

Foo (Foo (Bar End))

由于后者有两个连续的Foo。在单个数据声明中表达这一点的正确方法是什么?

2 个答案:

答案 0 :(得分:3)

使用单个声明,您需要一个索引类型:

data Foo : Bool -> Type where
  End : Foo True
  Bar : Foo True -> Foo False
  Foo : Foo False -> Foo True

这与以下两种相互类型的居民相同:

data FooTrue = End | Foo FooFalse
data FooFalse = Bar FooTrue

通常,n数据类型的共同族可以用具有n元素的类型索引的单个类型族表示。索引表示具有以下优点:它允许通过相互类型不可能的泛型操作,因为可以具有类型为{b : Bool} -> Foo b -> Foo b{b : Bool} -> Foo b -> Foo (not b)的转换。像长度索引列表这样的无限共同族也只能使用索引类型。

答案 1 :(得分:1)

一种可能的解决方案是使用索引类型:

data FooBarEnd : Nat -> Type where
  End : FooBarEnd 0
  Foo : FooBarEnd 1 -> FooBarEnd 0
  Bar : FooBarEnd 0 -> FooBarEnd 1

几个测试:

test1 : FooBarEnd 0
test1 = Foo (Bar (Foo (Bar End)))

test2 : FooBarEnd 0
test2 = End

test3 : FooBarEnd 1
test3 = Bar End

Foo (Foo (Bar End))不是FooBarEnd 0FooBarEnd 1的有效用语。

这种方法的缺点是必须使用索引01。 我不确定这是否是您正在寻找的解决方案。

如果您决定允许Foo End作为有效期限,则可以将定义更改为:

data FooBarEnd : Nat -> Type where
  End : FooBarEnd 1
  Foo : {auto prf : n `GTE` 1} -> FooBarEnd n -> FooBarEnd 0
  Bar : {auto prf : n `LTE` 1} -> FooBarEnd n -> FooBarEnd 2

其中GTELTE相应地大于或等于或小于或等于。

上述内容允许Foo EndBar End等保留您的原始限制,而且Idris能够自动推断出隐式证据条件prf

更为非正式地,(一元)构造函数Foo期望类型FooBarEnd 1FooBarEnd 2的值作为参数,这意味着我们只允许构建Foo EndFoo (Bar ...),因为我们将每个构造函数分隔为它们自己的"子类型",由自然数索引。构造函数Bar要求End(索引1< = 1)或Foo(索引0< = 1)。