通过Coq中列表的索引实现安全元素检索

时间:2014-05-20 10:32:06

标签: coq agda

我试图证明Agda中Coq Extraction机制和MAlonzo编译器之间代码生成的差异。我在Agda中想出了这个简单的例子:

data Nat : Set where
  zero : Nat
  succ : Nat → Nat

data List (A : Set) : Set where
  nil : List A
  cons : A → List A → List A

length : ∀ {A} → List A → Nat
length nil = zero
length (cons _ xs) = succ (length xs)

data Fin : Nat → Set where
  finzero : ∀ {n} → Fin (succ n)
  finsucc : ∀ {n} → Fin n → Fin (succ n)

elemAt : ∀ {A} (xs : List A) → Fin (length xs) → A
elemAt nil ()
elemAt (cons x _) finzero = x
elemAt (cons _ xs) (finsucc n) = elemAt xs n

直接翻译为Coq(absurd pattern emulation)产生:

Inductive Nat : Set :=
| zero : Nat
| succ : Nat -> Nat.

Inductive List (A : Type) : Type :=
| nil : List A
| cons : A -> List A -> List A.

Fixpoint length (A : Type) (xs : List A) {struct xs} : Nat :=
   match xs with
   | nil => zero
   | cons _ xs' => succ (length _ xs')
   end.

Inductive Fin : Nat -> Set :=
| finzero : forall n : Nat, Fin (succ n)
| finsucc : forall n : Nat, Fin n -> Fin (succ n).

Lemma finofzero : forall f : Fin zero, False.
Proof. intros a; inversion a. Qed.

Fixpoint elemAt (A : Type) (xs : List A) (n : Fin (length _ xs)) : A :=
   match xs, n with
   | nil, _ => match finofzero n with end
   | cons x _, finzero _ => x
   | cons _ xs', finsucc m n' => elemAt _ xs' n' (* fails *)
   end.

但elemAt的最后一个案例失败了:

File "./Main.v", line 26, characters 46-48:
Error:
In environment
elemAt : forall (A : Type) (xs : List A), Fin (length A xs) -> A
A : Type
xs : List A
n : Fin (length A xs)
a : A
xs' : List A
n0 : Fin (length A (cons A a xs'))
m : Nat
n' : Fin m
The term "n'" has type "Fin m" while it is expected to have type
 "Fin (length A xs')".

似乎Coq没有推断succ m = length A (cons A a xs')。我应该怎么 告诉Coq它会使用这些信息吗?或者我做的事情完全没有意义?

2 个答案:

答案 0 :(得分:2)

进行模式匹配相当于使用destruct策略。 您无法使用finofzero直接证明destruct

inversion策略在执行destruct之前会自动生成一些方程式。 然后它尝试做discriminate做的事情。结果非常混乱。

Print finofzero.
  • 要证明fin zero -> P之类的内容,您应首先将其更改为fin n -> n = zero -> P
  • 要证明list nat -> P(更常见的是forall l : list nat, P l),您无需将其更改为list A -> A = nat -> P,因为list的唯一参数是它的定义。
  • 要证明S n <= 0 -> False之类的内容,您应首先将其更改为S n1 <= n2 -> n2 = 0 -> False,因为<=的第一个参数是参数,而第二个参数不是。
  • 在目标f x = f y -> P (f y)中,要重写假设,首先需要将目标更改为f x = z -> f y = z -> P z,然后才能使用归纳法重写假设,因为第一个参数=(实际上是第二个)是=
  • 定义中的参数

尝试定义不带参数的<=,以了解归纳原理如何变化。 通常,在对谓词使用归纳之前,应确保它的参数是变量。否则信息可能会丢失。

Conjecture zero_succ : forall n1, zero = succ n1 -> False.
Conjecture succ_succ : forall n1 n2, succ n1 = succ n2 -> n1 = n2.

Lemma finofzero : forall n1, Fin n1 -> n1 = zero -> False.
Proof.
intros n1 f1.
destruct f1. 
intros e1.
eapply zero_succ.
eapply eq_sym.
eapply e1.
admit.
Qed.

(* Use the Show Proof command to see how the tactics manipulate the proof term. *)
Definition elemAt' : forall (A : Type) (xs : List A) (n : Nat), Fin n -> n = length A xs -> A.
Proof.
fix elemAt 2.
intros A xs.
destruct xs as [| x xs'].
intros n f e.
destruct (finofzero f e).
destruct 1.
intros e.
eapply x.
intros e.
eapply elemAt.
eapply H.
eapply succ_succ.
eapply e.
Defined.

Print elemAt'.

Definition elemAt : forall (A : Type) (xs : List A), Fin (length A xs) -> A :=
  fun A xs f => elemAt' A xs (length A xs) f eq_refl.

CPDT对此有更多了解。

如果在证据结束时Coq执行eta减少和beta / zeta减少(在范围内最多出现一次变量),也许情况会更清楚。

答案 1 :(得分:1)

我认为您的问题类似于Dependent pattern matching in coq。 Coq的match并没有太大的推断,所以你必须通过手工提供平等来帮助它。