Coq添加新变量而不是使用正确的变量

Coq添加新变量而不是使用正确的变量,coq,Coq,我正在研究我自己在Coq中的向量实现,我遇到了一个奇怪的问题 以下是我迄今为止的代码: Inductive Fin : nat -> Type := |FZ : forall n, Fin (S n) |FS : forall n, Fin n -> Fin (S n). Definition emptyf(A : Type) : Fin 0 -> A. intro e; inversion e. Defined. Inductive Vec(A : Type)

我正在研究我自己在Coq中的向量实现,我遇到了一个奇怪的问题

以下是我迄今为止的代码:

Inductive Fin : nat -> Type :=
  |FZ : forall n, Fin (S n)
  |FS : forall n, Fin n -> Fin (S n).

Definition emptyf(A : Type) : Fin 0 -> A.
  intro e; inversion e.
Defined.

Inductive Vec(A : Type) : nat -> Type :=
  |Nil  : Vec A 0
  |Cons : forall n, A -> Vec A n -> Vec A (S n).

Definition head(A : Type)(n : nat)(v : Vec A (S n)) : A :=
  match v with
  |Cons a _ => a
  end.

Definition tail(A : Type)(n : nat)(v : Vec A (S n)) : Vec A n :=
  match v with
  |Cons _ w => w
  end.

Fixpoint index(A : Type)(n : nat) : Vec A n -> Fin n -> A :=
  match n as n return Vec A n -> Fin n -> A with
  |0   => fun _ i => emptyf _ i
  |S m => fun v i => match i with
                     |FZ _ => head v
                     |FS j => index (tail v) j
                     end
  end.
直到
tail
的所有内容都可以很好地编译,但是当我尝试编译
索引时,我收到以下错误:

Error:
In environment
index : forall (A : Type) (n : nat), Vec A n -> Fin n -> A
A : Type
n : nat
m : nat
v : Vec A (S m)
i : Fin (S m)
n0 : nat
j : Fin n0
The term "j" has type "Fin n0" while it is expected to have type "Fin m".

显然,罪魁祸首是Coq引入了新变量
n0
,而不是分配
j
类型
Fin m
,即使这是
j
唯一可能的类型,这将导致
i
j
构建。你知道为什么会发生这种情况,以及我怎样才能解决这个问题吗?

我发现
Vec
Fin
通常很难使用,所以最近我使用了math comp中的
'I_n
n.-tuples T
,它们只是自然的和附有不相关证明的列表。但是,如果您想继续享受复杂模式匹配的乐趣,可以尝试定义更强大的模式匹配原则:

Definition fin_case T m (i : Fin m) : T -> (Fin (pred m) -> T) -> T :=
  match i with
  | FZ _   => fun fn fz => fn
  | FS _ j => fun fn fz => fz j
  end.
一旦有了
fin_case
,函数定义就可以工作了:

Fixpoint index (A : Type) (n : nat) : Vec A n -> Fin n -> A :=
  match n as n return Vec A n -> Fin n -> A with
  | 0   => fun _ i => emptyf _ i
  | S m => fun v i => fin_case i (head v) (index (tail v))
  end.

我发现一般来说很难使用
Vec
Fin
,所以这些天我使用了math comp中的
'I_n
n.-tuples T
,它们只是自然的和附带了不相关证明的列表。但是,如果您想继续享受复杂模式匹配的乐趣,可以尝试定义更强大的模式匹配原则:

Definition fin_case T m (i : Fin m) : T -> (Fin (pred m) -> T) -> T :=
  match i with
  | FZ _   => fun fn fz => fn
  | FS _ j => fun fn fz => fz j
  end.
一旦有了
fin_case
,函数定义就可以工作了:

Fixpoint index (A : Type) (n : nat) : Vec A n -> Fin n -> A :=
  match n as n return Vec A n -> Fin n -> A with
  | 0   => fun _ i => emptyf _ i
  | S m => fun v i => fin_case i (head v) (index (tail v))
  end.

使用
match
时,可能会丢失信息。我用这个方法把信息放回上下文中

    match i in Fin (S n0) return n0 = m  -> A with 
      ... => fun H : n0 = m => ...
    end  eq_refl
使Coq能够在上下文中获取信息
n0=m
。它作为函数参数发送到match子句中。要在类型检查中使用它,我使用
(将H与…end匹配)
,以便Coq了解
Fin n0=Fin m
。 这就是解决办法

Fixpoint index(A : Type)(n : nat) : Vec A n -> Fin n -> A :=

  match n as n return Vec A n -> Fin n -> A with
  |0   => fun _ i => emptyf _ i
  |S m => fun v i =>
           match i in Fin (S n') return n' = m  -> A with
             |FZ _ => fun _ => head _ _ v
             |FS _ j => fun H => index (tail v) (match H with eq_refl _  => j end)
           end eq_refl
  end.

当类型检查不理解两件事是相等的时,通常护航模式可以帮助您将该信息放入上下文中。我还建议使用
refine
逐步建立术语。它可以让您查看上下文中有哪些信息。

当您使用
match
时,可能会丢失信息。我用这个方法把信息放回上下文中

    match i in Fin (S n0) return n0 = m  -> A with 
      ... => fun H : n0 = m => ...
    end  eq_refl
使Coq能够在上下文中获取信息
n0=m
。它作为函数参数发送到match子句中。要在类型检查中使用它,我使用
(将H与…end匹配)
,以便Coq了解
Fin n0=Fin m
。 这就是解决办法

Fixpoint index(A : Type)(n : nat) : Vec A n -> Fin n -> A :=

  match n as n return Vec A n -> Fin n -> A with
  |0   => fun _ i => emptyf _ i
  |S m => fun v i =>
           match i in Fin (S n') return n' = m  -> A with
             |FZ _ => fun _ => head _ _ v
             |FS _ j => fun H => index (tail v) (match H with eq_refl _  => j end)
           end eq_refl
  end.

当类型检查不理解两件事是相等的时,通常护航模式可以帮助您将该信息放入上下文中。我还建议使用
refine
逐步建立术语。它让您看到上下文中有哪些信息。

注意,您不需要对
n
进行模式匹配,而只需要对
Fin n
类型的参数进行模式匹配。由此产生的定义更简单

Fixpoint index {A:Type} {n:nat} (i:Fin n) : Vec A n -> A :=
  match i in Fin n0 return Vec A n0 -> A with
  | FZ => fun v => head v
  | FS j => fun v => index j (tail v)
  end.
Coq实际上足够精确,可以猜测注释

Fixpoint index {A:Type} {n:nat} (i:Fin n) : Vec A n -> A :=
  match i with
  | FZ => fun v => head v
  | FS j => fun v => index j (tail v)
  end.

请注意,您不需要对
n
进行模式匹配,而只需要对类型为
Fin n
的参数进行模式匹配。由此产生的定义更简单

Fixpoint index {A:Type} {n:nat} (i:Fin n) : Vec A n -> A :=
  match i in Fin n0 return Vec A n0 -> A with
  | FZ => fun v => head v
  | FS j => fun v => index j (tail v)
  end.
Coq实际上足够精确,可以猜测注释

Fixpoint index {A:Type} {n:nat} (i:Fin n) : Vec A n -> A :=
  match i with
  | FZ => fun v => head v
  | FS j => fun v => index j (tail v)
  end.

除了其他答案之外,还有一个基于策略的解决方案:

Fixpoint index (A : Type) (n : nat) (v : Vec A n) (i : Fin n) : A.
  destruct v as [| n h tl].
  - exact (emptyf A i).
  - inversion i as [ | ? i'].
    + exact h.
    + exact (index _ _ tl i').
Defined.
inversion
策略负责“信息丢失”。如果您尝试打印索引。
结果不会很好,但Coq基本上使用了@larsr提到的护航模式


请注意,这种方法不在
n
上使用模式匹配。相反,它的模式与向量参数匹配,这就是为什么它不需要
head
tail
函数的原因。

只是为了补充其他答案,一个基于策略的解决方案:

Fixpoint index (A : Type) (n : nat) (v : Vec A n) (i : Fin n) : A.
  destruct v as [| n h tl].
  - exact (emptyf A i).
  - inversion i as [ | ? i'].
    + exact h.
    + exact (index _ _ tl i').
Defined.
inversion
策略负责“信息丢失”。如果您尝试打印索引。结果不会很好,但Coq基本上使用了@larsr提到的护航模式


请注意,这种方法不在
n
上使用模式匹配。相反,它的模式与向量参数匹配,这就是为什么它不需要
head
tail
函数的原因。

谢谢你的回答,但在我看来,这只是把问题推到了如何定义
fin_case
上。不管我怎么做,我还是会遇到同样的问题。你介意分享一下你心中的定义吗?更新后,有几条评论:a)注意,这种“将问题推上”的方式确实不止于此,而且是Coq风格相关编程中的一个关键工具。b) 解决这类问题还有很多其他方法,事实上,写一个合适的匹配仍然感觉像是一门艺术。谢谢你的回答,但在我看来,这只是把问题推到了如何定义
fin\u case
。不管我怎么做,我还是会遇到同样的问题。你介意分享一下你心中的定义吗?更新后,有几条评论:a)注意,这种“将问题推上”的方式确实不止于此,而且是Coq风格相关编程中的一个关键工具。b) 还有很多其他方法可以解决这类问题,事实上,编写一个合适的匹配仍然感觉像是一门艺术。一个简单的
定义cast(mn:nat)(i:Fin m)(H:m=n):Fin n:=eq_rec m Fin i n H。
可能会让代码更容易阅读(
(将H与eq_refl匹配)
(cast j H)
)。对使用等式转换的解决方案(包括带反转的解决方案)的评论是,它们可能无法在Coq内正确计算/减少,需要额外重写。一个简单的
定义转换(mn:nat)(i:Fin m)(H:m=n):Fin n:=eq_rec m Fin i n H.
可能会使代码更易于阅读(
(将H与eq匹配_