我如何检查策略生成条款的可兑换性?

How do I check for convertibility in a tactic producing terms?

假设我有以下策略来检查一个术语是否为零:

Ltac isZero x :=
  match x with
  | O => constr:true
  | _ => constr:false
  end.

Goal Set.
  let isz := isZero O in pose isz.
  (* adds true to the context *)

现在想象一下,我想让策略接受更多一点;也许任何可以用零转换的术语。如果这是针对目标的战术,我会做

Ltac isZero x :=
  match x with
  | ?v => unify v 0; constr:true
  | _  => constr:false
  end.

但这对于产生术语的策略来说是失败的:

Error: Value is a term. Expected a tactic. 

如何检查策略生成条款的可兑换性?在这个具体示例中,减少 x 或计算它 (let xx := eval compute in x) 可能有效,但在更复杂的示例中,计算成本可能会令人望而却步,尤其是当我需要减少比较的两项时。

PS:供参考,未简化的问题是我正在尝试有效地查找可能与 FMap 中的值匹配的键,该 FMap 是由对 add 的调用序列构建的,策略看起来像

Ltac find_key value :=
  match fmap with
  | add ?k value _ => constr:(Some k)
  | add _ _ ?m => find_key value m
  | _ => constr:None
  end

使用此实现,如果映射包含可转换为 value 但在语法上不等于它的术语而不是 value,则该策略将不正确 return None .

您可以尝试构造一个触发转换检查的术语;例如:

Goal 2 + 2 = 4.

match goal with
| |- ?a = ?b =>
  let e := constr:(eq_refl a : a = b) in
  idtac "equal"
| |- _ => idtac "not equal"
end.

通常,这会打印 "equal"。但是,如果您在上面的目标中将 4 替换为 3,内部分支将失败,打印 "not equal".