类型族的归纳定义

Sve*_*son 1 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
Run Code Online (Sandbox Code Playgroud)

我从中定义了一系列类型:

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).
Run Code Online (Sandbox Code Playgroud)

这里的理性是,给定一个正则表达式,r:Exp我试图表示与r一个类型相关联的语言Language r,而我正在使用一个归纳定义.

我想证明:

Lemma L1 : forall (c:char)(x:Language (Lit c)),
  x = LangLit c.
Run Code Online (Sandbox Code Playgroud)

(换言之,类型Language (Lit c)仅具有一个元素,即正则表达式的语言'c'是由单一的字符串的"c".当然,我需要定义一些语义转换的元素Language rstring)

现在这个问题的具体细节并不重要,只是用来激发我的问题:让我们使用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).
Run Code Online (Sandbox Code Playgroud)

在这里,我再次使用单个归纳定义来定义一系列类型List n.

我想证明:

Lemma L2: forall (x: List 0),
  x = ListNil.
Run Code Online (Sandbox Code Playgroud)

(换句话说,该类型List 0只有一个元素).

我对这个想法已经用完了.

通常,当试图用归纳类型(或谓词)证明(负)结果时,我会使用elim策略(确保所有相关假设都在我的目标(generalize)中,并且只有变量出现在类型构造函数中).但elim在这种情况下并不好.

Art*_*rim 5

如果你愿意接受的不仅仅是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.
Run Code Online (Sandbox Code Playgroud)

如果你检查这个术语,你会发现这个策略依赖于JMeq_eq公理来获得证据:

Print Assumptions l0.

Axioms:
JMeq_eq : forall (A : Type) (x y : A), x ~= y -> x = y
Run Code Online (Sandbox Code Playgroud)

幸运的是l0,通过对前一个引理的陈述做一个小的改动,可以证明不必求助于Coq基本逻辑之外的特征.

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.
Run Code Online (Sandbox Code Playgroud)

我们可以看到这个新证明不需要任何额外的公理:

Print Assumptions l0'.
Closed under the global context
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?这个问题,粗略地说,就是在勒柯克我们不能依赖类型,它们的指数具有特定的形状方面进行案例分析(如0,你的情况)直接.相反,我们必须证明一个更普遍的陈述,其中有问题的指数被变量所取代.这正是l0_gen引理所做的.注意我们如何在n返回一个抽象的函数时进行匹配v.这是所谓的"护卫模式"的另一个例子.我们写的

match n with
| 0 => v = @Vector.nil A
| _ => True
end.
Run Code Online (Sandbox Code Playgroud)

Coq会v0分支中看到有类型Vector.t A n,使得该分支生病.

提出这样的概括是在Coq中进行依赖类型编程的一大难点.其他系统,例如Agda,可以用更少的努力编写这种代码,但最近才证明这可以在不依赖Coq希望避免包括在其基本理论中的额外公理的情况下完成.我们只能希望在将来的版本中简化这一点.

  • 我也找到了这个替代证明:eq_dep_eq; 移动E:{1 2} 0 v => iz v; case:iz/v E.有时很有用.实际上,在使用依赖类型时,`eq_dep`和类似的技巧非常有用. (3认同)