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

Coq adding a new variable instead of using the correct one

我正在 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 之前的所有内容都可以正常编译,但是当我尝试编译 index 时,我收到以下错误:

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,即使这是 [=15= 的唯一可能类型] ] 这将导致从 j 构建 i。知道为什么会发生这种情况吗?我该如何解决这个问题?

我发现 VecFin 通常很难使用,所以我最近使用 math-comp 中的 'I_nn.-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 时,您可能会丢失信息。我使用 convoy pattern 将信息返回到上下文中。

    match i in Fin (S n0) return n0 = m  -> A with 
      ... => fun H : n0 = m => ...
    end  eq_refl

使 Coq 能够将信息 n0 = m 放入上下文中。它作为函数参数发送到匹配子句中。为了在类型检查中使用它,我使用 (match H with ... 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.

只是添加到其他答案中,一个基于策略的解决方案:

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 策略负责 "information loss"。如果你尝试 Print index. 结果不会很好,但 Coq 本质上使用了@larsr 提到的护航模式。

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