|
| 1 | +======================================================================== |
| 2 | +Notations |
| 3 | +======================================================================== |
| 4 | + |
| 5 | +Notations are user-declared surface-syntax forms for formulas. A |
| 6 | +declaration binds a name prefixed by ``#`` to a template of literal |
| 7 | +punctuation and slots; uses of the notation at the call site are parsed |
| 8 | +against the template and expanded to an underlying body at type-checking |
| 9 | +time. Notations live in the same namespace as operators and |
| 10 | +abbreviations: they follow ``import``/``export``/``clone`` rules, they |
| 11 | +can be declared ``local`` inside a ``section``, and the pretty-printer |
| 12 | +automatically renders expanded bodies back to template syntax whenever |
| 13 | +possible. |
| 14 | + |
| 15 | +------------------------------------------------------------------------ |
| 16 | +Anatomy of a declaration |
| 17 | +------------------------------------------------------------------------ |
| 18 | + |
| 19 | +.. admonition:: Syntax |
| 20 | + |
| 21 | + | ``local``? ``notation`` ``#``\ *name* |
| 22 | + | *tyvars*? |
| 23 | + | *binder-slots*? |
| 24 | + | *form-slots*\ * |
| 25 | + | *template* |
| 26 | + | (``:`` *codom*)? |
| 27 | + | ``=`` *body* ``.`` |
| 28 | +
|
| 29 | +Every piece except the name, template, and body is optional. The |
| 30 | +declaration mirrors an operator declaration with an extra *template* |
| 31 | +section that describes the surface syntax. |
| 32 | + |
| 33 | +Example:: |
| 34 | + |
| 35 | + notation #pair (a : int) (b : int) " [" a ", " b "] " = (a, b). |
| 36 | + |
| 37 | + lemma L (x y : int) : #pair [x, y] = (x, y). |
| 38 | + proof. by trivial. qed. |
| 39 | + |
| 40 | +------------------------------------------------------------------------ |
| 41 | +Template literals and punctuations |
| 42 | +------------------------------------------------------------------------ |
| 43 | + |
| 44 | +A template is a sequence that alternates **STRING literals** and **slot |
| 45 | +references**. The first element of the template must be a STRING. |
| 46 | + |
| 47 | +Each STRING must lex to exactly one of six punctuation tokens: |
| 48 | + |
| 49 | + ``[`` ``]`` ``:`` ``|`` ``,`` ``;`` |
| 50 | + |
| 51 | +Additional whitespace *inside* the string is ignored by the input |
| 52 | +matcher but preserved verbatim by the pretty-printer. For example, |
| 53 | +``" [ "`` and ``"["`` both match a single ``[`` at the call site, but |
| 54 | +the former pretty-prints with spaces on both sides. |
| 55 | + |
| 56 | +A STRING that lexes to zero tokens, two or more tokens, or an |
| 57 | +unsupported symbol is rejected at declaration time:: |
| 58 | + |
| 59 | + notation #bad " @" a " @ " = a. |
| 60 | + (* rejected: notation punctuation `" @"' must lex to one of: [ ] : | , ; *) |
| 61 | + |
| 62 | +------------------------------------------------------------------------ |
| 63 | +Slots |
| 64 | +------------------------------------------------------------------------ |
| 65 | + |
| 66 | +A slot is a bare lowercase identifier inside the template. Each slot |
| 67 | +must be declared earlier in the notation as either a **form slot** or a |
| 68 | +**binder slot**. |
| 69 | + |
| 70 | +Form slots |
| 71 | + Declared with ``( name : ty )`` in the usual function-argument style. |
| 72 | + At the call site the slot consumes a formula; at typing time the |
| 73 | + formula is checked against ``ty``. |
| 74 | + |
| 75 | +Binder slots |
| 76 | + Declared inside ``#< ... >`` before the form-slot block, for example |
| 77 | + ``#< i : int >`` or ``#< i : int, j : int >``. A binder slot reads |
| 78 | + an *identifier* at the call site. The chosen identifier becomes a |
| 79 | + locally-bound name visible to any form slot that depends on it. |
| 80 | + |
| 81 | +Per-slot binder dependencies |
| 82 | + A form slot can declare that it depends on binder slots via |
| 83 | + ``==>``:: |
| 84 | + |
| 85 | + notation #mapI #< i : int > (n : int) (f : i ==> int) |
| 86 | + " [" i " : " n " : " f "] " = |
| 87 | + map f (iota_ 0 n). |
| 88 | + |
| 89 | + At typing, ``f`` has type ``int -> int`` (curried over the binders |
| 90 | + it declares). At the call site the user writes the body of the |
| 91 | + lambda in terms of the chosen binder name, and the expansion wraps |
| 92 | + it automatically:: |
| 93 | + |
| 94 | + lemma M : #mapI [k : 4 : k + 1] = map (fun k => k + 1) (iota_ 0 4). |
| 95 | + proof. by trivial. qed. |
| 96 | + |
| 97 | +**Template ordering constraint.** A form slot that depends on a binder |
| 98 | +``i`` must appear *after* ``i`` in the template. Forward references |
| 99 | +are rejected at declaration time. |
| 100 | + |
| 101 | +**Slot kind is inferred from the declaration, not the template.** A |
| 102 | +template reference like ``i`` means "ident slot" iff ``i`` is in the |
| 103 | +``#< ... >`` group; otherwise it means "form slot". |
| 104 | + |
| 105 | +------------------------------------------------------------------------ |
| 106 | +Type parameters |
| 107 | +------------------------------------------------------------------------ |
| 108 | + |
| 109 | +Notations may be polymorphic. Type parameters are declared in the same |
| 110 | +``['a 'b]`` syntax used by operators, appearing before the slot lists:: |
| 111 | + |
| 112 | + notation #ppair ['a 'b] (a : 'a) (b : 'b) " [" a ", " b "] " = (a, b). |
| 113 | + |
| 114 | + lemma L (x : int) (y : bool) : #ppair [x, y] = (x, y). |
| 115 | + proof. by trivial. qed. |
| 116 | + |
| 117 | +Type parameters are freshened at every call site, so independent uses |
| 118 | +do not leak type equalities. |
| 119 | + |
| 120 | +------------------------------------------------------------------------ |
| 121 | +Semantics |
| 122 | +------------------------------------------------------------------------ |
| 123 | + |
| 124 | +A notation is compiled to a regular operator of kind ``OB_nott`` that |
| 125 | +records the template alongside the typed body. At a call site |
| 126 | +``#name [ ... ]``: |
| 127 | + |
| 128 | +1. The parser looks up the template for ``#name`` and consumes tokens |
| 129 | + according to its items, collecting the slot arguments. |
| 130 | +2. The resulting ``PFntt`` node is type-checked. For each form slot |
| 131 | + with binder dependencies, the argument is type-checked in an |
| 132 | + environment extended with the user-chosen binder idents, then |
| 133 | + wrapped in ``fun`` over those idents. For each ident slot, a fresh |
| 134 | + local is created with the user-chosen name. The body is then |
| 135 | + obtained by substituting all slot idents with their resolved |
| 136 | + values and alpha-renaming each bound binder to the user-chosen |
| 137 | + name. |
| 138 | + |
| 139 | +Because expansion happens at typing, slot-level errors attribute to the |
| 140 | +user's source and not to the declaration's body. |
| 141 | + |
| 142 | +------------------------------------------------------------------------ |
| 143 | +Locality |
| 144 | +------------------------------------------------------------------------ |
| 145 | + |
| 146 | +A declaration may be prefixed with ``local`` inside a ``section``; the |
| 147 | +notation is then discharged when the section closes:: |
| 148 | + |
| 149 | + section. |
| 150 | + local notation #tmp (a : int) " [" a "] " = a + 1. |
| 151 | + lemma inside : #tmp [3] = 4. |
| 152 | + proof. by trivial. qed. |
| 153 | + end section. |
| 154 | + (* #tmp is no longer visible here. *) |
| 155 | + |
| 156 | +Outside a section, ``local notation`` is rejected, as for other |
| 157 | +operators. |
| 158 | + |
| 159 | +------------------------------------------------------------------------ |
| 160 | +Pretty-printing |
| 161 | +------------------------------------------------------------------------ |
| 162 | + |
| 163 | +The pretty-printer reverse-matches the body of each registered notation |
| 164 | +against each formula it prints. When a formula matches the body of a |
| 165 | +notation, the printer renders it using the template, honouring |
| 166 | +whitespace from the declaration's STRING literals:: |
| 167 | + |
| 168 | + notation #pair (a : int) (b : int) " [" a ", " b "] " = (a, b). |
| 169 | + |
| 170 | + lemma L (x y : int) : #pair [x, y] = (x, y). |
| 171 | + |
| 172 | + print L. |
| 173 | + (* lemma L: forall (x y : int), #pair [x, y] = #pair [x, y] . *) |
| 174 | + |
| 175 | +For notations with binder slots, the printer unwraps the matched |
| 176 | +lambdas to recover the user's chosen binder names. |
| 177 | + |
| 178 | +------------------------------------------------------------------------ |
| 179 | +Examples |
| 180 | +------------------------------------------------------------------------ |
| 181 | + |
| 182 | +A polymorphic pair:: |
| 183 | + |
| 184 | + notation #pair ['a 'b] (a : 'a) (b : 'b) " [" a ", " b "] " = (a, b). |
| 185 | + |
| 186 | + lemma L (x : int) (y : bool) : #pair [x, y] = (x, y). |
| 187 | + proof. by trivial. qed. |
| 188 | + |
| 189 | +A polymorphic map notation with an explicit lambda:: |
| 190 | + |
| 191 | + notation #pmap ['a 'b] (f : 'a -> 'b) (xs : 'a list) |
| 192 | + " [" f " : " xs "] " = |
| 193 | + map f xs. |
| 194 | + |
| 195 | + lemma L : #pmap [(fun x => x + 1) : [1; 2; 3]] = [2; 3; 4]. |
| 196 | + proof. by trivial. qed. |
| 197 | + |
| 198 | +A binder-slot map notation where the user-chosen name scopes into the |
| 199 | +body form:: |
| 200 | + |
| 201 | + notation #mapI #< i : int > (n : int) (f : i ==> int) |
| 202 | + " [" i " : " n " : " f "] " = |
| 203 | + map f (iota_ 0 n). |
| 204 | + |
| 205 | + lemma L : #mapI [k : 4 : k + 1] = map (fun k => k + 1) (iota_ 0 4). |
| 206 | + proof. by trivial. qed. |
| 207 | + |
| 208 | +A notation inside a section:: |
| 209 | + |
| 210 | + section. |
| 211 | + |
| 212 | + notation #gpair (a : int) (b : int) " [" a ", " b "] " = (a, b). |
| 213 | + local notation #lpair (a : int) (b : int) " [" a ", " b "] " = (a, b). |
| 214 | + |
| 215 | + lemma t_g : #gpair [1, 2] = (1, 2). |
| 216 | + proof. by trivial. qed. |
| 217 | + |
| 218 | + lemma t_l : #lpair [1, 2] = (1, 2). |
| 219 | + proof. by trivial. qed. |
| 220 | + |
| 221 | + end section. |
| 222 | + |
| 223 | + lemma after : #gpair [3, 4] = (3, 4). |
| 224 | + proof. by trivial. qed. |
| 225 | + |
| 226 | +------------------------------------------------------------------------ |
| 227 | +Error catalogue |
| 228 | +------------------------------------------------------------------------ |
| 229 | + |
| 230 | +The following errors may appear at notation-declaration or call time. |
| 231 | + |
| 232 | +``notation punctuation `"x"' must lex to one of: [ ] : | , ;`` |
| 233 | + A template STRING could not be lexed to exactly one of the six |
| 234 | + accepted punctuation tokens. |
| 235 | + |
| 236 | +``template references unknown slot: `x'`` |
| 237 | + The template refers to a slot name that is not declared as either a |
| 238 | + binder slot (``#< ... >``) or a form slot (``( ... )``). |
| 239 | + |
| 240 | +``form slot depends on binder `i', which must be declared earlier in the template`` |
| 241 | + A form slot's ``==>`` dependency was listed, but the binder slot |
| 242 | + appears *after* the form slot in the template. Re-order the |
| 243 | + template. |
| 244 | + |
| 245 | +``parse error: unknown notation `#name'`` |
| 246 | + The call site used a notation name that is not in scope. The |
| 247 | + notation may not be ``import``-ed, may be ``local`` and out of |
| 248 | + scope, or simply not declared. |
| 249 | + |
| 250 | +``parse error: in notation `#name': expected `:'`` |
| 251 | + The call site did not match the template at the indicated punct. |
| 252 | + Check that the call reproduces the declaration template verbatim. |
| 253 | + |
| 254 | +``parse error: in notation `#name': expected identifier for binder slot `i'`` |
| 255 | + A binder slot consumes an identifier at the call site; another |
| 256 | + token was supplied. |
| 257 | + |
| 258 | +------------------------------------------------------------------------ |
| 259 | +Limitations |
| 260 | +------------------------------------------------------------------------ |
| 261 | + |
| 262 | +- The punctuation palette is fixed to the six tokens listed above. |
| 263 | + Arbitrary keywords or operator symbols are not accepted because the |
| 264 | + lexer tokenizes ahead of the notation-aware parser. |
| 265 | + |
| 266 | +- Templates cannot describe iteration (e.g. ``item*`` or |
| 267 | + ``item, ...``) or optional parts. Each declaration has a single, |
| 268 | + fixed shape. |
| 269 | + |
| 270 | +- Within a single theory file loaded via ``require``, a notation |
| 271 | + declared on line *N* is visible from line *N + 1* onward. In |
| 272 | + interactive mode the visibility is immediate. |
| 273 | + |
| 274 | +- A ``local notation`` must appear inside a ``section``, as for other |
| 275 | + ``local`` operators. |
0 commit comments