我对下面h定义中证明词的匹配部分的Coq类型系统的行为感到困惑:
Set Implicit Arguments.
Definition h := fun (a b : nat) (e : a = b) =>
(fun (x y : nat)(HC : b = y)(H : x = y) =>
(match H in (_ = y0) return (b = y0 -> b = x) with
| @eq_refl _ _ => fun HC0 : b = x => HC0
end HC)) a b (eq_refl b) e.
检查h告诉我们整体类型是" forall a b:nat,a = b - > b = a"。
由于H的类型是x = y,看起来匹配将返回类型为b = y的项 - >由于return子句,b = x。应用后面的各种术语后,我们得到h的预期类型。
但是,有趣的是HC0:b = x => HC0是b = x - >的类型的同一性函数。 b = x。我不相信有强迫b = x - >的任何强制行为。 b = x被识别为类型b = y - > b = x。
我最好的猜测是H的构造函数是x = x的@eq_refl nat x,它是唯一的。由于H也是x = y类型,因此名称x和y绑定到相同的术语。因此,类型系统决定b = x - > b = x的类型为b = y - > b = x。这很接近吗?这种行为是在某处解释或记录的吗?我看了iota减少,但我不认为这是正确的。
答案 0 :(得分:5)
这就是它。记录了这种行为(在the manual中查找“匹配......与......结构”),虽然理解那里发生的事情可能有点令人生畏。
首先,回想一下在Coq中检查典型match
的方法:
Inductive list (T : Type) :=
| nil : list T
| cons : T -> list T -> list T.
Definition tail (T : Type) (l : list T) : list T :=
match l with
| nil => nil
| cons x l' => l'
end.
Coq检查(1)list
类型的每个构造函数在match
中都有对应的分支,(2)每个分支具有相同的类型(在这种情况下,{{1假设每个分支中引入的构造函数参数具有适当的类型(这里假设list T
具有类型x
而T
在第二个分支中具有类型l'
)。
在这种简单的情况下,用于检查每个分支的类型与整个匹配表达式的类型完全相同。但是, not 始终为true:有时,Coq根据从正在检查的分支中提取的信息使用更专业的类型。在对索引归纳类型进行案例分析时经常会发生这种情况,例如list T
:
eq
(Inductive eq (T : Type) (x : T) : T -> Prop :=
| eq_refl : eq T x x.
表示法只是=
的中缀语法糖。)
赋予冒号右侧的归纳类型的参数在Coq中是特殊的:它们被称为 indices 。出现在左侧的那些(在本例中为eq
和T
)称为参数。参数在归纳类型的声明中必须全部不同,并且必须与所有构造函数的结果中使用的参数完全匹配。例如,请考虑以下非法代码段:
x
Coq拒绝此示例,因为它在Inductive eq' (T : Type) (x : T) : T -> Type :=
| eq_refl' : eq nat 4 3.
构造函数的结果中找到了nat
而不是T
。
指数不具有此限制:出现在构造函数的返回类型上的索引可以是适当类型的任何表达式。此外,该表达式可能因我们所在的构造函数而异。因此,Coq允许每个分支的返回类型根据每个分支的索引的选择而变化。请考虑原始示例的以下略微简化版本。
eq_refl'
由于Definition h (a b : nat) (e : a = b) : b = a :=
match e in _ = x return x = a with
| eq_refl => eq_refl : a = a
end.
的第二个参数是索引,因此它原则上可以根据使用的构造函数而变化。由于我们只在查看使用的构造函数时才发现该索引实际是什么,因此Coq允许匹配的返回类型依赖于该索引:匹配的eq
子句为所有索引提供名称一个归纳类型,这些名称成为可以在in
子句中使用的绑定变量。
在键入分支时,Coq会找出索引的值,并将这些值替换为return
子句中声明的变量。此匹配只有一个分支,该分支强制索引等于in
类型中的第二个参数(在本例中为e
)。因此,Coq尝试确保该分支的类型为a
(即a = a
,x = a
替换a
)。因此,我们可以简单地提供x
,我们就完成了。
现在Coq检查了所有分支都是正确的,它为整个匹配表达式分配了eq_refl : a = a
子句的类型,其中return
的索引替换为e
。此变量x
的类型为e
,索引为a = b
,因此结果类型为b
(即b = a
x = a
取代b
)。
This answer提供了有关参数和索引之间差异的更多解释,如果有帮助的话。