我尝试试验列表的定义。 例如,让我们看一下这个定义:
Inductive list1 : Type -> Type := nil1 : forall (A : Type), list1 A
| cons1 : forall (A : Type), A -> list1 A -> list1 A.
您可能会认为上面的定义与此等效:
Inductive list0 (A : Type) : Type := nil0 : list0 A
| cons0 : A -> list0 A -> list0 A.
为什么要这张地图:
Fixpoint map0 {A : Type} {B : Type} (f : A -> B) (xs : list0 A) : list0 B :=
match xs with
nil0 _ => nil0 B
| cons0 _ v ys => cons0 B (f v) (map0 f ys)
end.
被接受,但是这个不是:
Fail Fixpoint map1 {A : Type} {B : Type} (f : A -> B) (xs : list1 A) :=
match xs with
nil1 _ => nil1 B
| cons1 _ v ys => cons1 B (f v) (map1 f ys)
end.
?
答案 0 :(得分:1)
这确实是数据类型定义的一个令人困惑的方面。问题在于,list1
不等同于list0
,因为在这些方法中如何处理 indices 和 parameters 定义。在Coq术语中,“索引”表示在冒号右侧声明的参数,如list1
中所示。相反,“参数”是在冒号左侧声明的参数,如A
中的list0
。
使用索引时,match
表达式的返回类型对于索引必须是通用的。可以从list1_rec
的类型中看出这一点,它是用于在列表上编写递归定义的组合器:
Check list1_rec.
list1_rec
: forall P : forall T : Type, list1 T -> Set,
(forall A : Type, P A (nil1 A)) ->
(forall (A : Type) (a : A) (l : list1 A), P A l -> P A (cons1 A a l)) ->
forall (T : Type) (l : list1 T), P T l
此类型表示给定由列表和元素P
索引的通用类型l : list1 A
,您可以通过告诉Coq {{1}返回什么来产生P A l
类型的结果}}和nil1
。但是,cons1
分支的类型(cons1
的第三个参数)表明,该分支不仅必须适用于以list1
类型出现的A
,而且还适用于所有 other 类型l
。将此与A'
的类型进行比较:
list0_rec
Check list0_rec.
list0_rec
: forall (A : Type) (P : list0 A -> Set),
P (nil0 A) ->
(forall (a : A) (l : list0 A), P l -> P (cons0 A a l)) ->
forall l : list0 A, P l
的分支没有cons0
位,这意味着该分支只需要为forall A
中的A
类型工作。
这在编写诸如l : list0 A
之类的函数时会有所不同。在map
中,我们被允许应用map0
,因为我们知道f : A -> B
的参数类型为cons0
。在A
中,map1
的参数具有不同的通用类型cons1
,导致出现此错误消息:
A'
答案 1 :(得分:0)
为完整起见,您可以在map
上定义函数list1
:
Fixpoint map1 {A : Type} {B : Type} (f : A -> B) (xs : list1 A) :=
match xs with
| nil1 A' => fun _ => nil1 B
| cons1 A' v ys => fun f => cons1 B (f v) (map1 f ys)
end f.
这是所谓的convoy pattern的示例。通常,需要在return
构造中添加match
子句,以便进行类型检查,但是Coq足够聪明来推断它。
但是,我当然不赞成使用列表的这种定义,因为在类似情况下使用起来很麻烦。