Coq / Proof General中的Agda编程?

时间:2017-01-24 19:55:55

标签: coq agda dependent-type proof-general coqide

与Agda不同,Coq倾向于将证明与功能分开。 Coq给出的策略非常适合编写证明,但我想知道是否有办法复制一些Agda模式功能。

具体来说,我想:

  • 相当于Agda的?或Haskell的_,我可以在编写时省略函数的一部分,并且(希望)让Coq告诉我需要放在那里的类型
  • 在Agda模式(reify)中等效的C-c C-r,其中使用函数填充?块,并且它将为所需参数创建新的?
  • 当我在函数中执行match时,让Coq自动写入扩展可能的分支(如Agda模式中的C-c C-a)

这可能在CoqIde或Proof General中吗?

2 个答案:

答案 0 :(得分:6)

正如ejgallego在评论中所建议的,你可以(几乎)这样做。有company-coq工具,可以在ProofGeneral上工作。

让我演示如何使用company-coq和map策略实现refine函数。从

开始
Fixpoint map {A B} (f : A -> B) (xs : list A) : list B.

键入refine ().,然后将光标放在parens中并键入 Cc Ca RET list RET - 它会在填充了孔的列表中插入match表达式手动(让我们填写列表名称和基本情况)。

Fixpoint map {A B} (f : A -> B) (xs : list A) : list B.
  refine (match xs with
          | nil => nil
          | cons x x0 => cons _ _
          end).

要完成此操作,我们会将x0重命名为tl并提供递归案例exact (map A B f tl).

Fixpoint map {A B} (f : A -> B) (xs : list A) : list B.
  refine (match xs with
          | nil => nil
          | cons x tl => cons _ _
          end).
  exact (f x).
  exact (map A B f tl).
Defined.

还有一个有用的键盘快捷键 C-c C-a C-x ,它有助于将当前目标提取到单独的引理/辅助函数中。

答案 1 :(得分:5)

让我教你一个奇怪的伎俩。它可能不是你所有问题的答案,但它可能会有所帮助,至少在概念上是这样。

让我们实现自然数的加法,后者由

给出
Inductive nat : Set :=
  | zero : nat
  | suc : nat -> nat.

您可以尝试通过策略来编写添加内容,但这种情况会发生。

Theorem plus' : nat -> nat -> nat.
Proof.
  induction 1.

plus' < 2 subgoals

  ============================
   nat -> nat

subgoal 2 is:
 nat -> nat
你无法看到你在做什么。

诀窍是仔细观察你正在做的事情。我们可以引入一个无偿的依赖类型,克隆nat

Inductive PLUS (x y : nat) : Set :=
  | defPLUS : nat -> PLUS x y.

我们的想法是PLUS x y是“计算plus x y的方式”。我们需要一个投影,允许我们提取这种计算的结果。

Theorem usePLUS : forall x y, PLUS x y -> nat.
Proof.
  induction 1.
    exact n.
Defined.

现在我们已准备好通过证明进行编程。

Theorem mkPLUS : forall x y, PLUS x y.
Proof.

mkPLUS < 1 subgoal

  ============================
   forall x y : nat, PLUS x y

目标的结论向我们展示了我们当前的左手边和背景。 Agda中C-c C-c的类似物是......

  induction x.

mkPLUS < 2 subgoals

  ============================
   forall y : nat, PLUS zero y

subgoal 2 is:
 forall y : nat, PLUS (suc x) y

你可以看到它完成了案件分割!让我们敲掉基本案例。

    intros y.
      exact (defPLUS zero y    y).

调用PLUS的构造函数就像写一个方程式。想象一下在第三个参数之前的=符号。对于步骤情况,我们需要进行递归调用。

    intros y.
      eapply (fun h => (defPLUS (suc x) y    (suc (usePLUS x y h)))).

要进行递归调用,我们使用我们想要的参数调用usePLUS,这里xy,但是我们抽象了第三个参数,这是实际的解释计算它。我们只剩下那个子目标,实际上是终止检查。

mkPLUS < 1 subgoal

  x : nat
  IHx : forall y : nat, PLUS x y
  y : nat
  ============================
   PLUS x y

现在,您不是使用Coq的防范检查,而是使用......

        auto.

...检查归纳假设是否涵盖递归调用。我们

Defined.

我们有一个工人,但我们需要一个包装器。

Theorem plus : nat -> nat -> nat.
Proof.
  intros x y.
    exact (usePLUS x y (mkPLUS x y)).
Defined.

我们准备好了。

Eval compute in (plus (suc (suc zero)) (suc (suc zero))).

Coq <      = suc (suc (suc (suc zero)))
     : nat

拥有交互式构建工具。您可以对其进行游戏,通过使类型更具信息性来向您展示您正在解决的问题的相关细节。由此产生的校对脚本......

Theorem mkPLUS : forall x y, PLUS x y.
Proof.
  induction x.
    intros y.
      exact             (defPLUS zero    y    y).
    intros y.
      eapply (fun h =>  (defPLUS (suc x) y    (suc (usePLUS x y h)))).
        auto.
Defined.

...明确说明它构建的程序。你可以看到这是定义的补充。

如果你为程序构建自动化这个设置,然后在界面上显示你正在构建的程序和关键的问题简化策略,你会得到一个名为Epigram 1的有趣的小编程语言。