我试图证明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它会使用这些信息吗?或者我做的事情完全没有意义?
答案 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
并没有太大的推断,所以你必须通过手工提供平等来帮助它。