我想创建一个包含交替构造函数Foo
和Bar
的数据类型。例如,有效成员将是:
Foo (Bar (Foo (Bar End)))
但不是:
Foo (Foo (Bar End))
由于后者有两个连续的Foo。在单个数据声明中表达这一点的正确方法是什么?
答案 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 0
和FooBarEnd 1
的有效用语。
这种方法的缺点是必须使用索引0
和1
。
我不确定这是否是您正在寻找的解决方案。
如果您决定允许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
其中GTE
和LTE
相应地大于或等于或小于或等于。
上述内容允许Foo End
,Bar End
等保留您的原始限制,而且Idris能够自动推断出隐式证据条件prf
。
更为非正式地,(一元)构造函数Foo
期望类型FooBarEnd 1
或FooBarEnd 2
的值作为参数,这意味着我们只允许构建Foo End
或Foo (Bar ...)
,因为我们将每个构造函数分隔为它们自己的"子类型",由自然数索引。构造函数Bar
要求End
(索引1
< = 1)或Foo
(索引0
< = 1)。