Coq:以“%”和“ mod”为表示符号

时间:2019-05-15 04:44:31

标签: coq notation

我正在尝试为模等价关系定义一种表示法:

Inductive mod_equiv : nat -> nat -> nat -> Prop :=
  | mod_intro_same : forall m n, mod_equiv m n n
  | mod_intro_plus_l : forall m n1 n2, mod_equiv m n1 n2 -> mod_equiv m (m + n1) n2
  | mod_intro_plus_r : forall m n1 n2, mod_equiv m n1 n2 -> mod_equiv m n1 (m + n2).

(* 1 *) Notation "x == y 'mod' z" := (mod_equiv z x y) (at level 70).
(* 2 *) Notation "x == y % z" := (mod_equiv z x y) (at level 70).
(* 3 *) Notation "x == y %% z" := (mod_equiv z x y) (at level 70).

所有三个符号均被Coq接受。但是,在某些情况下,我无法使用该符号陈述定理:

(* 1 *)
Theorem mod_equiv_sym : forall (m n p : nat), n == p mod m -> p == n mod m.
(* Works fine as-is, but gives error if `Arith` is imported before:
   Syntax error: 'mod' expected after [constr:operconstr level 200] (in [constr:operconstr]).
*)

(*************************************)

(* 2 *)
Theorem mod_equiv_sym : forall (m n p : nat), n == p % m -> p == n % m.
(* Gives the following error:
   Syntax error: '%' expected after [constr:operconstr level 200] (in [constr:operconstr]).
*)

(*************************************)

(* 3 *)
Theorem mod_equiv_sym : forall (m n p : nat), n == p %% m -> p == n %% m.
(* Works just fine. *)
  1. modCoq.Init.Nat都在顶层定义。为什么新符号Coq.Arith.PeanoNat在一种环境中适用而在另一种环境中无效?

  2. 符号x == y 'mod' z与内置的%冲突,但是Coq解析器给出的错误消息几乎与%情况相同,并且该消息在这两种情况下都不是很有帮助。这是预期的行为吗? IMO,如果解析器无法在如此琐碎的上下文中理解一种表示法,那么该表示法就不应首先被接受。

1 个答案:

答案 0 :(得分:2)

您的第一个问题有一个简单的答案。 Coq的初始状态(部分)由Coq.Init.Prelude确定,其中(as of this answer)包含行

Require Coq.Init.Nat.

这就是说,Coq.Init.Prelude不导入,仅与Require一起提供。仅当导入包含符号的模块时,该符号才有效。如果将该符号声明为本地(Local Notation ...),则即使导入该模块也不会激活该符号。


第二个问题比较棘手,并深入研究Coq如何解析符号。让我们从一个有效的示例开始。在Coq.Init.Notations中(实际上是在Coq.Init.Prelude中导入的)中,保留了符号“ x <= y

Reserved Notation "x <= y < z" (at level 70, y at next level).

在Coq.Init.Peano(也已导入)中,给该符号赋予了含义。我们实际上不会担心这部分,因为我们主要关注解析。

要查看保留符号的作用,可以使用白话命令Print Grammar constr.。这将显示一长串用于解析constr(Coq语法的基本单位)的内容。可以在列表的下方找到该符号的条目。

| "70" RIGHTA
  [ SELF;  "?="; NEXT
  [...]
  | SELF;  "<="; NEXT;  "<"; NEXT
  [...]
  | SELF;  "="; NEXT ]

我们看到该表示法是正确的关联(RIGHTA 1 并处于70级。我们还看到,该表示法中的三个变量x,{ {1}}和y分别在70级(z),71级(SELF)和71级(NEXT)处解析。 2

在解析期间,Coq从级别0开始,并查看下一个令牌。直到存在应该在当前级别解析的令牌之前,该级别才会增加。因此,级别较低的符号优先于级别较高的符号。 (从概念上讲,这是如何工作的-可能已经做了一些优化)。

当找到复杂的符号时,例如在“ 5 <=”之后,解析器会记住该符号的语法 3 NEXT。在“ 5 <=”之后,我们在71层解析SELF; "<="; NEXT; "<"; NEXT,这意味着如果没有任何事情低于71层,我们将停止尝试解析y并继续。

在那之后,下一个标记必须是“ <”,然后如果它是z,我们将在级别71处解析z。

关卡的伟大之处在于它不需要括号即可与其他符号进行交互。例如,在代码y中,我们不需要括号,因为1 * 2 < 3 + 4 <= 5 * 6*被声明为较低的级别(分别为40和50)。因此,当我们解析+时(在第71级),我们可以解析所有y,然后再转到3 + 4。另外,当我们解析<= z时,我们可以捕获z,因为5 * 6的解析级别比*的解析级别低。


好的,现在我们知道了,我们可以弄清楚您的情况。

导入Arith(或Nat)后,z成为一种符号。具体来说,我们在第40级有一个左联想符号,其语法为mod(使用SELF; "mod"; NEXT进行检查)。定义Print Grammar constr.表示法时,该条目在70级与语法mod正确关联。中间部分仅表示SELF; "=="; constr:operconstr LEVEL "200"; "mod"; NEXT在第200级进行了解析(作为一个解释-就像我们刚才讨论的所有其他内容一样。)

因此,在解析y时,我们会很好地解析n == p mod m,然后在200级开始解析n ==。由于Arith的y仅在40级,这就是我们的方法将解析mod。但是随后我们的p mod m表示法就没有了。我们已经到了声明的结尾,x == y mod z尚未被解析。

mod z

(错误现在更有意义了吗?)

如果您确实要使用Syntax error: 'mod' expected after [constr:operconstr level 200] (in [constr:operconstr]). 表示法,同时仍使用Arith的mod表示法,则需要在较低的层次上解析y。由于mod处于40级,我们可以通过{p>使x mod y成为39级

y

由于算术运算的级别为40或更高,这意味着我们需要使用括号写Notation "x == y 'mod' z" := (mod_equiv z x y) (at level 70, y at level 39).

对于您的“%”表示法,这将很困难。 “%”通常用于范围定界(例如5 == (3 * 4) mod 7),并在级别1上非常紧密地绑定。您可以在级别0上进行(x + y)%nat解析,但这意味着根本不能将任何符号用于y不带括号。如果可以接受,请继续。

由于“ %%”没有任何冲突(在标准库中),因此您可以在任何方便的地方随意使用它。您可能想对y进行较低级别的解析(y很标准),但这不是必需的。


  1. 默认为右关联。显然,Coq的解析器没有“无关联性”选项,因此,即使您明确地说出“无关联性”,它仍会注册为正确的关联性。在实践中,这通常不会造成麻烦。

  2. 这就是为什么在“下一级y”保留符号的原因。默认情况下,表示法中间的变量在级别200进行解析,这可以通过保留类似y at next level的表示法并使用Reserved Notation "x ^ y ^ z" (at level 70).来查看解析级别来看到。我们将看到,Print Grammar constr.就是这种情况。

  3. 如果多个符号以“ 5 <=”开头怎么办?级别较低的那个显然会被采用,但是如果它们具有相同的级别,它将尝试两者并在不解析的情况下回溯。但是,如果一个符号完成了,即使该选择以后会引起麻烦,它也不会回退。我不确定确切的规则,但是我怀疑这取决于首先声明哪种符号。