Coq/Proof 中的类似 Agda 的编程一般?
Agda-like programming in Coq/Proof General?
与 Agda 不同,Coq 倾向于将证明与函数分开。 Coq 给出的策略非常适合编写证明,但我想知道是否有一种方法可以复制一些 Agda 模式的功能。
具体来说,我想要:
- Agda 的
?
或 Haskell 的 _
的一些等价物,我可以在编写函数时省略部分函数,并且(希望)让 Coq 告诉我需要放在那里的类型
- 等同于 Agda 模式(具体化)中的 C-c C-r,你用一个函数填充
?
块,它会为所需的参数创建新的 ?
块
- 当我在函数中执行
match
时,让 Coq 自动写扩展可能的分支(如 Agda 模式中的 C-c C-a)
在 CoqIde 或 Proof General 中这可能吗?
正如 ejgallego 在评论中所建议的那样,您(几乎)可以做到。有 company-coq 工具,它在 ProofGeneral 之上运行。
让我演示如何使用 company-coq 和 refine
策略来实现 map
功能。从
开始
Fixpoint map {A B} (f : A -> B) (xs : list A) : list B.
键入 refine ().
,然后将光标放在括号内并键入 C-c C-a 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 这有助于将当前目标提取到单独的 lemma/helper函数。
让我教你一个奇怪的技巧。它可能无法解决您所有的问题,但至少在概念上可能有所帮助。
让我们实现自然数的加法,后者由
给出
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
是 "the way to compute 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
进球的结论向我们展示了我们目前的 left-hand 方面和背景。 C-c C-c
在 Agda 中的类比是...
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的构造函数就像写一个等式。想象一下在它的第三个参数之前有一个 =
符号。对于step case,我们需要进行递归调用。
intros y.
eapply (fun h => (defPLUS (suc x) y (suc (usePLUS x y h)))).
为了进行递归调用,我们用我们想要的参数调用usePLUS
,这里是x
和y
,但是我们抽象了第三个参数,这是对实际上如何计算它。我们只剩下那个子目标,实际上是终止检查。
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.
...明确说明了它构建的程序。你可以看到这是定义加法。
如果您将此设置自动化以构建程序,然后在显示您正在构建的程序和关键 problem-simplifying 策略的界面上分层,您会得到一种有趣的小型编程语言,称为 Epigram 1。
与 Agda 不同,Coq 倾向于将证明与函数分开。 Coq 给出的策略非常适合编写证明,但我想知道是否有一种方法可以复制一些 Agda 模式的功能。
具体来说,我想要:
- Agda 的
?
或 Haskell 的_
的一些等价物,我可以在编写函数时省略部分函数,并且(希望)让 Coq 告诉我需要放在那里的类型 - 等同于 Agda 模式(具体化)中的 C-c C-r,你用一个函数填充
?
块,它会为所需的参数创建新的?
块 - 当我在函数中执行
match
时,让 Coq 自动写扩展可能的分支(如 Agda 模式中的 C-c C-a)
在 CoqIde 或 Proof General 中这可能吗?
正如 ejgallego 在评论中所建议的那样,您(几乎)可以做到。有 company-coq 工具,它在 ProofGeneral 之上运行。
让我演示如何使用 company-coq 和 refine
策略来实现 map
功能。从
Fixpoint map {A B} (f : A -> B) (xs : list A) : list B.
键入 refine ().
,然后将光标放在括号内并键入 C-c C-a 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 这有助于将当前目标提取到单独的 lemma/helper函数。
让我教你一个奇怪的技巧。它可能无法解决您所有的问题,但至少在概念上可能有所帮助。
让我们实现自然数的加法,后者由
给出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
是 "the way to compute 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
进球的结论向我们展示了我们目前的 left-hand 方面和背景。 C-c C-c
在 Agda 中的类比是...
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的构造函数就像写一个等式。想象一下在它的第三个参数之前有一个 =
符号。对于step case,我们需要进行递归调用。
intros y.
eapply (fun h => (defPLUS (suc x) y (suc (usePLUS x y h)))).
为了进行递归调用,我们用我们想要的参数调用usePLUS
,这里是x
和y
,但是我们抽象了第三个参数,这是对实际上如何计算它。我们只剩下那个子目标,实际上是终止检查。
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.
...明确说明了它构建的程序。你可以看到这是定义加法。
如果您将此设置自动化以构建程序,然后在显示您正在构建的程序和关键 problem-simplifying 策略的界面上分层,您会得到一种有趣的小型编程语言,称为 Epigram 1。