以 returns 值的策略检查 evars

Check for evars in a tactic that returns a value

我正在尝试编写一个 return 是一个值的策略,并且在这样做的过程中它需要检查某些东西是否是 evar。

不幸的是,我不能使用 is_evar,因为那样的话该策略不被视为 return 一个值(而是另一种策略)。下面是一个例子。

有什么建议吗?

Ltac reify_wrt values ls :=
  match ls with
  | nil => constr:(@nil nat)
  | ?a :: ?ls' => let i := lookup a values in
                 let idx := reify_wrt values ls' in
                 constr:(i :: idx)
  | ?e :: ?ls' => is_evar e; 
                  let i := constr:(100) in 
                  let idx := reify_wrt values ls' in
                  constr:(i :: idx)
  end.

这是 Ltac 的一个众所周知的限制:您不能编写有时 returns 一个值,有时 returns 另一个策略的策略。解决方案是用 continuation-passing 风格 重写你的策略。不幸的是,我无法详细解释如何执行此操作,但是 Adam Chlipala 的 CDPT 有一个 chapter on Ltac 描述了这个问题;只需在文本中查找 "continuation"。

在 Coq >= 8.5 中,您可以使用一个简洁(或讨厌)的小技巧将策略执行插入到 constr 构造中:将其包装在 match goal.

Ltac reify_wrt values ls :=
  match ls with
  | nil => constr:(@nil nat)
  | ?a :: ?ls' => let i := lookup a values in
                 let idx := reify_wrt values ls' in
                 constr:(i :: idx)
  | ?e :: ?ls' => let check := match goal with _ => is_evar e end in
                  let i := constr:(100) in 
                  let idx := reify_wrt values ls' in
                  constr:(i :: idx)
  end.

因为编程语言的奥秘让我着迷,现在我将告诉您比您可能想知道的更多关于 Ltac 现在和过去的执行模型的信息。

战术评估有两个阶段:战术表达式评估和战术运行。在战术 运行 期间,执行排序、优化、重写等。在策略表达式评估期间,如果在策略表达式的头部位置找到以下构造,则对以下构造进行评估:

  • 战术调用是resolved/followed/inlined/unfolded
  • let ... in ... 将其参数的表达式求值绑定到名称,并进行替换
  • constr:(...) 被评估和类型检查
  • lazymaytch ... with ... end 被求值(带回溯),returns 第一个匹配的分支成功表达式求值
  • match ... with ... end 被评估(带回溯) 并且分支被急切地执行 。请注意,在这张图片中,match 很奇怪,因为它强制提前执行策略。如果您曾在 Coq < 8.5 中看到 "immediate match producing tactics not allowed in local definitions",那是一条错误消息,明确禁止我在上面利用的行为。我猜这是因为 match 的这种奇怪行为是实现 Ltac 的原始开发人员想要隐藏的疣。因此,在 Coq 8.4 中,您唯一可以注意到它的地方是,如果您将 match 放在 lazymatch 中并尝试失败级别,并注意到 lazymatch 将在策略执行失败时回溯内部 match 当你通常认为它会失败时。

在 Coq 8.5 中,重写了策略引擎以处理依赖子目标。这引起了 ; 语义的细微变化,只有在使用多个目标之间共享的 evar 时才能观察到。在重写中,开发人员将 lazymatch 的语义更改为“match 无回溯”,并取消了对 "immediate match producing tactics." 的限制因此你可以做一些奇怪的事情,例如:

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 也急切地评估它的分支。