简体   繁体   English

arguments在coq中的结构特性证明

[英]Proofs of structural properties of arguments in match in coq

I would like to write a safe zip function in coq that accepts the argument length equality as an argument.我想在 coq 中编写一个安全的 zip function ,它接受参数长度相等作为参数。

Fixpoint zip {b a:Type} (proof : length l1 = length l2) (l1 : list a) (l2 : list b) : list (a * b) :=
  match l1,l2 with
  | nil,nil => nil
  | cons a a',cons b b' => cons (a,b) (zip a' b')
  | _,_ => (* never reached *)
  end.

what is the general approach to this kind of problem?解决此类问题的一般方法是什么? I would appreciate comments and resources on using refinement types in the context of coq functions generally.我会很感激关于在 coq 函数的上下文中使用细化类型的评论和资源。

Here is the best approach to this particular problem, in my opinion:在我看来,这是解决这个特定问题的最佳方法:

Require Import Coq.Lists.List.
Import ListNotations.

Fixpoint zip_pre {A B} (xs : list A) (ys : list B) : list (A * B) :=
  match xs, ys with
  | x :: xs, y :: ys => (x, y) :: zip_pre xs ys
  | _, _ => []
  end.

Definition zip {A B} (xs : list A) (ys : list B) (_ : length xs = length ys) :=
  zip_pre xs ys.

In words, we first define a version of zip that does not care about lengths, and then we use that to define the function you're looking for.换句话说,我们首先定义一个不关心长度的zip版本,然后我们使用它来定义您正在寻找的 function。

This might feel like cheating;这可能感觉像是在作弊; after all, the zip function doesn't even use its proof argument: Here is another version that is perhaps closer to what you were originally looking for:毕竟, zip function 甚至没有使用它的证明论点:这是另一个可能更接近您最初寻找的版本:

Fixpoint zip' {A B} (xs : list A) (ys : list B) :
    length xs = length ys -> list (A * B) :=
  match xs, ys with
  | x :: xs, y :: ys =>
    fun H : S (length xs) = S (length ys) =>
      (x, y) :: zip' xs ys ltac:(congruence)
  | [], [] => fun _ => []
  | x :: xs, [] => fun H : S (length xs) = 0 => ltac:(easy)
  | [], y :: ys => fun H : 0 = S (length ys) => ltac:(easy)
  end.

Unlike zip , zip' uses its proof argument in two ways.zip不同, zip'以两种方式使用其证明参数。 In the contradictory cases, it invokes some tactic code ( ltac:(easy) ) to argue that this case cannot arise.在矛盾的情况下,它会调用一些策略代码( ltac:(easy) )来论证这种情况不会出现。 In the recursive case, it needs to find a proof of length xs = length ys to apply the recursive call;在递归的情况下,它需要找到一个length xs = length ys来应用递归调用; for this, it uses the congruence tacitc.为此,它使用了congruence默契。

Why is zip better than zip' ?为什么zipzip'好? Its code is shorter and easier to read.它的代码更短,更容易阅读。 In particular, note how zip' has a match returning a function.特别要注意zip'如何匹配返回 function。 This idiom, known as the convoy pattern , is needed whenever we need to refine the type of an argument in a pattern matching branch.每当我们需要在模式匹配分支中细化参数的类型时,都需要这个习语,称为convoy 模式 Actually, zip' is even worse than what you might think, because the tactics that discharge the proof obligations generate code.实际上, zip'比你想象的还要糟糕,因为解除证明义务的策略会生成代码。 Try printing zip' to see what the definition really looks like, Sadly: this ugliness is not just cosmetic.尝试打印zip'以查看定义的真正样子,可悲的是:这种丑陋不仅仅是装饰性的。 these more complicated definitions are much harder to reason about, For instance, it is possible to prove that zip and zip' always produce the same outputs.这些更复杂的定义更难推理,例如,可以证明zipzip'总是产生相同的输出。 Try it to see how fun it is!试试看它有多有趣!

To be fair, there are Coq plugins that make it easier to write this sort of code (eg the Equations plugin).公平地说,有一些 Coq 插件可以更轻松地编写此类代码(例如Equations插件)。 But at the end of the day they will still generate code that is equivalent to zip' .但归根结底,他们仍然会生成相当于zip'代码。 And in this case, the length hypothesis doesn't buy us much.在这种情况下,长度假设并没有给我们带来太多好处。

In general, one is better off avoiding dependent types in Coq, unless there is a strong argument that justifies the additional complexity.一般来说,最好避免在 Coq 中使用依赖类型,除非有强有力的论据证明额外的复杂性是合理的。 For instance, in the case of zip , suppose that you have some code that uses a lot of different lists of the same length n .例如,在zip的情况下,假设您有一些代码使用许多相同长度的不同列表n You might want to argue that zip has an inverse:您可能想争辩说zip有一个逆:

let xys := zip xs ys in
(map fst xys, map snd xys) = (xs, ys)

It is not possible to prove this result unless we know that xs and ys have the same length.除非我们知道xsys的长度相同,否则无法证明这个结果。 We can add an additional hypothesis to our lemma, or we can work with length-indexed lists.我们可以在引理中添加一个额外的假设,或者我们可以使用长度索引列表。 Here is one possible definition:这是一种可能的定义:

Definition vec A n := {l : list A | length l = n}.

The {.. |..} is Coq's notation for refinement, or subset types. {.. |..}是 Coq 用于细化或子集类型的符号。 We can then repackage some of the functions over lists to work over vec .然后我们可以重新打包列表中的一些函数以在vec上工作。 For instance, we can show that map takes vec A n to vec B n .例如,我们可以证明mapvec A n带到vec B n This approach pays off if you don't use many functions that require you to change the length index n , because in those cases you need to reason about the equality of complicated length expressions on types, which Coq is not very good at.如果您不使用许多需要更改长度索引n的函数,这种方法是有回报的,因为在这些情况下,您需要推理类型上复杂长度表达式的相等性,而 Coq 不太擅长这一点。 For vec in particular, I would recommend you to have a look at the tuple library of mathcomp (available here ), which provides a good example of how this pattern can be used at scale.特别是对于vec ,我建议您查看 mathcomp 的tuple库(可在此处获得),它提供了如何大规模使用此模式的一个很好的示例。

Edit编辑

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM