类型族的归纳定义
Inductive definition for family of types
我已经为此苦苦挣扎了一段时间。我有一个归纳型:
Definition char := nat.
Definition string := list char.
Inductive Exp : Set :=
| Lit : char -> Exp
| And : Exp -> Exp -> Exp
| Or : Exp -> Exp -> Exp
| Many: Exp -> Exp
我从中归纳定义了一个类型族:
Inductive Language : Exp -> Set :=
| LangLit : forall c:char, Language (Lit c)
| LangAnd : forall r1 r2: Exp, Language(r1) -> Language(r2) -> Language(And r1 r2)
| LangOrLeft : forall r1 r2: Exp, Language(r1) -> Language(Or r1 r2)
| LangOrRight : forall r1 r2: Exp, Language(r2) -> Language(Or r1 r2)
| LangEmpty : forall r: Exp, Language (Many r)
| LangMany : forall r: Exp, Language (Many r) -> Language r -> Language (Many r).
这里的合理性是给定一个正则表达式 r:Exp
我试图将与 r
关联的语言表示为一个类型 Language r
,并且我这样做是用一个归纳定义。
我想证明:
Lemma L1 : forall (c:char)(x:Language (Lit c)),
x = LangLit c.
(换句话说,类型Language (Lit c)
只有一个元素,即正则表达式'c'
的语言是由单个字符串"c"
组成的。当然我需要定义一些将 Language r
的元素转换为 string
)
的语义
现在这个问题的细节并不重要,只是用来激发我的问题:让我们使用 nat
而不是 Exp
并让我们定义一个类型 List n
代表长度列表 n
:
Parameter A:Set.
Inductive List : nat -> Set :=
| ListNil : List 0
| ListCons : forall (n:nat), A -> List n -> List (S n).
这里我再次使用一个归纳定义来定义一个类型族List n
。
我想证明:
Lemma L2: forall (x: List 0),
x = ListNil.
(换句话说,类型List 0
只有一个元素)。
我 运行 对此一无所知。
通常当尝试用归纳类型(或谓词)证明(否定)结果时,我会使用 elim
策略(确保所有相关假设都在我的目标范围内(generalize
) 并且只有变量出现在类型构造函数中)。但是 elim
在这种情况下并不好。
如果你愿意接受的不仅仅是 Coq 的基本逻辑,你可以使用 dependent destruction
策略,在 Program
库中可用(我冒昧地重新措辞你最后一个关于标准库向量的例子):
Require Coq.Vectors.Vector.
Require Import Program.
Lemma l0 A (v : Vector.t A 0) : v = @Vector.nil A.
Proof.
now dependent destruction v.
Qed.
如果你检查这个术语,你会发现这个策略依赖于 JMeq_eq
公理来获得通过的证明:
Print Assumptions l0.
Axioms:
JMeq_eq : forall (A : Type) (x y : A), x ~= y -> x = y
幸运的是,无需求助于 Coq 基本逻辑之外的特征就可以证明 l0
,只需对前面引理的陈述做一个小改动即可。
Lemma l0_gen A n (v : Vector.t A n) :
match n return Vector.t A n -> Prop with
| 0 => fun v => v = @Vector.nil A
| _ => fun _ => True
end v.
Proof.
now destruct v.
Qed.
Lemma l0' A (v : Vector.t A 0) : v = @Vector.nil A.
Proof.
exact (l0_gen A 0 v).
Qed.
我们可以看到这个新证明不需要任何额外的公理:
Print Assumptions l0'.
Closed under the global context
这里发生了什么?粗略地说,问题在于,在 Coq 中,我们无法根据索引具有特定形状(例如 0,在您的情况下)直接 的依赖类型执行案例分析。相反,我们必须证明一个更一般的陈述,其中有问题的索引被变量替换。这正是 l0_gen
引理所做的。请注意我们如何必须在 n
return 上进行匹配,这是一个在 v
上抽象的函数。这是所谓的 "convoy pattern" 的另一个实例。如果我们写了
match n with
| 0 => v = @Vector.nil A
| _ => True
end.
Coq 会将 0
分支中的 v
视为具有类型 Vector.t A n
,从而使该分支类型错误。
提出这样的概括是在 Coq 中进行依赖类型编程的一大难题。其他系统,例如 Agda,可以更轻松地编写此类代码,但直到最近 shown 才可以在不依赖 Coq 想要避免包含在其代码中的额外公理的情况下完成此操作基础理论。我们只能希望在以后的版本中能简化这一点。
我已经为此苦苦挣扎了一段时间。我有一个归纳型:
Definition char := nat.
Definition string := list char.
Inductive Exp : Set :=
| Lit : char -> Exp
| And : Exp -> Exp -> Exp
| Or : Exp -> Exp -> Exp
| Many: Exp -> Exp
我从中归纳定义了一个类型族:
Inductive Language : Exp -> Set :=
| LangLit : forall c:char, Language (Lit c)
| LangAnd : forall r1 r2: Exp, Language(r1) -> Language(r2) -> Language(And r1 r2)
| LangOrLeft : forall r1 r2: Exp, Language(r1) -> Language(Or r1 r2)
| LangOrRight : forall r1 r2: Exp, Language(r2) -> Language(Or r1 r2)
| LangEmpty : forall r: Exp, Language (Many r)
| LangMany : forall r: Exp, Language (Many r) -> Language r -> Language (Many r).
这里的合理性是给定一个正则表达式 r:Exp
我试图将与 r
关联的语言表示为一个类型 Language r
,并且我这样做是用一个归纳定义。
我想证明:
Lemma L1 : forall (c:char)(x:Language (Lit c)),
x = LangLit c.
(换句话说,类型Language (Lit c)
只有一个元素,即正则表达式'c'
的语言是由单个字符串"c"
组成的。当然我需要定义一些将 Language r
的元素转换为 string
)
现在这个问题的细节并不重要,只是用来激发我的问题:让我们使用 nat
而不是 Exp
并让我们定义一个类型 List n
代表长度列表 n
:
Parameter A:Set.
Inductive List : nat -> Set :=
| ListNil : List 0
| ListCons : forall (n:nat), A -> List n -> List (S n).
这里我再次使用一个归纳定义来定义一个类型族List n
。
我想证明:
Lemma L2: forall (x: List 0),
x = ListNil.
(换句话说,类型List 0
只有一个元素)。
我 运行 对此一无所知。
通常当尝试用归纳类型(或谓词)证明(否定)结果时,我会使用 elim
策略(确保所有相关假设都在我的目标范围内(generalize
) 并且只有变量出现在类型构造函数中)。但是 elim
在这种情况下并不好。
如果你愿意接受的不仅仅是 Coq 的基本逻辑,你可以使用 dependent destruction
策略,在 Program
库中可用(我冒昧地重新措辞你最后一个关于标准库向量的例子):
Require Coq.Vectors.Vector.
Require Import Program.
Lemma l0 A (v : Vector.t A 0) : v = @Vector.nil A.
Proof.
now dependent destruction v.
Qed.
如果你检查这个术语,你会发现这个策略依赖于 JMeq_eq
公理来获得通过的证明:
Print Assumptions l0.
Axioms:
JMeq_eq : forall (A : Type) (x y : A), x ~= y -> x = y
幸运的是,无需求助于 Coq 基本逻辑之外的特征就可以证明 l0
,只需对前面引理的陈述做一个小改动即可。
Lemma l0_gen A n (v : Vector.t A n) :
match n return Vector.t A n -> Prop with
| 0 => fun v => v = @Vector.nil A
| _ => fun _ => True
end v.
Proof.
now destruct v.
Qed.
Lemma l0' A (v : Vector.t A 0) : v = @Vector.nil A.
Proof.
exact (l0_gen A 0 v).
Qed.
我们可以看到这个新证明不需要任何额外的公理:
Print Assumptions l0'.
Closed under the global context
这里发生了什么?粗略地说,问题在于,在 Coq 中,我们无法根据索引具有特定形状(例如 0,在您的情况下)直接 的依赖类型执行案例分析。相反,我们必须证明一个更一般的陈述,其中有问题的索引被变量替换。这正是 l0_gen
引理所做的。请注意我们如何必须在 n
return 上进行匹配,这是一个在 v
上抽象的函数。这是所谓的 "convoy pattern" 的另一个实例。如果我们写了
match n with
| 0 => v = @Vector.nil A
| _ => True
end.
Coq 会将 0
分支中的 v
视为具有类型 Vector.t A n
,从而使该分支类型错误。
提出这样的概括是在 Coq 中进行依赖类型编程的一大难题。其他系统,例如 Agda,可以更轻松地编写此类代码,但直到最近 shown 才可以在不依赖 Coq 想要避免包含在其代码中的额外公理的情况下完成此操作基础理论。我们只能希望在以后的版本中能简化这一点。