在 Coq >= 8.5 中,你可以使用一个巧妙(或讨厌)的小技巧将策略执行插入到 constr 构造中:将其包装在match goal
.
Ltac reify_wrt 值 ls :=
匹配 ls
| nil => constr:(@nil nat)
| ?a :: ?ls' => 让 i := 在其中查找一个值
让 idx := reify_wrt values ls' in
constr:(i :: idx)
| ?e :: ?ls' => let check := match goal with _ => is_evar e end in
让 i := constr:(100) 在
让 idx := reify_wrt values ls' in
constr:(i :: idx)
结尾。
因为编程语言的奥秘让我着迷,所以我现在要告诉你的可能比你想知道的更多关于 Ltac 现在和过去的执行模型。
战术评估有两个阶段:战术表达评估和战术运行。在战术运行期间,执行排序、优化、重写等。在策略表达式评估期间,如果在策略表达式的头部位置找到以下结构,则会评估以下结构:
- 战术调用被解决/跟随/内联/展开
let ... in ...
将其参数的表达式评估绑定到名称,并进行替换
constr:(...)
被评估和类型检查
lazymaytch ... with ... end
被评估(带有回溯),并返回成功表达式评估的第一个匹配分支
match ... with ... end
被评估(带有回溯)并且分支被急切地执行。请注意,在这张图片中,match
它很奇怪,因为它迫使策略的执行提前到来。如果您曾经在 Coq < 8.5 中看到“本地定义中不允许立即匹配生成策略”,那是一条错误消息,明确禁止我在上面利用的行为。我猜这是因为这种奇怪的行为match
是实现 Ltac 的原始开发人员想要隐藏的一个缺陷。因此,您在 Coq 8.4 中唯一可以注意到它的地方是,如果您坚持match
内部lazymatch
并使用失败级别,并注意当您通常认为它会失败时,它lazymatch
会回溯到内部战术执行失败。match
在 Coq 8.5 中,战术引擎被重写以处理相关子目标。这引起了语义上的细微变化,;
只有在使用多个目标之间共享的 eva 时才能观察到这些变化。在重写中,开发者将语义更改为lazymatch
“match
不回溯”,并取消了对“立即匹配产生策略”的限制。因此,您可以做一些奇怪的事情,例如:
let dummy := match goal with _ => rewrite H end in
constr:(true)
并具有产生副作用的策略。但是,您不能再执行以下操作:
let tac := lazymatch b with
| true => tac1
| false => tac2
end in
tac long args.
因为在 Coq >= 8.5 中,lazymatch
也急切地评估它的分支。