@@ -45,10 +45,13 @@ used by the `@[simps]` attribute.
45
45
- To change the default value, see Note [custom simps projection].
46
46
- You are strongly discouraged to add this attribute manually.
47
47
- The first argument is the list of names of the universe variables used in the structure
48
- - The second argument is the expressions that correspond to the projections of the structure
49
- (these can contain the universe parameters specified in the first argument).
48
+ - The second argument is a list that consists of
49
+ - a custom name for each projection of the structure
50
+ - an expressions for each projections of the structure (definitionally equal to the
51
+ corresponding projection). These expressions can contain the universe parameters specified
52
+ in the first argument).
50
53
-/
51
- @[user_attribute] meta def simps_str_attr : user_attribute unit (list name × list expr) :=
54
+ @[user_attribute] meta def simps_str_attr : user_attribute unit (list name × list (name × expr) ) :=
52
55
{ name := `_simps_str ,
53
56
descr := " An attribute specifying the projection of the given structure." ,
54
57
parser := do e ← texpr, eval_pexpr _ e }
@@ -77,6 +80,11 @@ attribute [notation_class* coe_fn] has_coe_to_fun
77
80
Get the projections used by `simps` associated to a given structure `str`. The second component is
78
81
the list of projections, and the first component the (shared) list of universe levels used by the
79
82
projections.
83
+
84
+ The returned information is also stored in a parameter of the attribute `@[_simps_str]`, which
85
+ is given to `str`. If `str` already has this attribute, the information is read from this
86
+ attribute instead.
87
+
80
88
The returned universe levels are the universe levels of the structure. For the projections there
81
89
are three cases
82
90
* If the declaration `{structure_name}.simps.{projection_name}` has been declared, then the value
@@ -102,15 +110,31 @@ attribute [notation_class* coe_fn] has_coe_to_fun
102
110
```
103
111
def equiv.simps.inv_fun {α β} (e : α ≃ β) : β → α := e.symm
104
112
```
113
+
114
+ Optionally, this command accepts two optional arguments
115
+ * If `trace_if_exists` the command will always generate a trace message when the structure already
116
+ has the attribute `@[_simps_str]`.
117
+ * The `name_changes` argument accepts a list of pairs `(old_name, new_name)`. This is used to
118
+ change the projection name `old_name` to the custom projection name `new_name`. Example:
119
+ for the structure `equiv` the projection `to_fun` could be renamed `apply`. This name will be
120
+ used for parsing and generating projection names. This argument is ignored if the structure
121
+ already has an existing attribute.
105
122
-/
106
123
-- if performance becomes a problem, possible heuristic: use the names of the projections to
107
124
-- skip all classes that don't have the corresponding field.
108
- meta def simps_get_raw_projections (e : environment) (str : name) :
109
- tactic (list name × list expr) := do
125
+ meta def simps_get_raw_projections (e : environment) (str : name) (trace_if_exists : bool := ff)
126
+ (name_changes : list (name × name) := []) : tactic (list name × list (name × expr) ) := do
110
127
has_attr ← has_attribute' `_simps_str str,
111
128
if has_attr then do
112
- when_tracing `simps .verbose trace!" [simps] > found projection information for structure {str}" ,
113
- simps_str_attr.get_param str
129
+ data ← simps_str_attr.get_param str,
130
+ to_print ← data.2 .mmap $ λ s, to_string <$> pformat!" Projection {s.1}: {s.2}" ,
131
+ let to_print := string.join $ to_print.intersperse " \n > " ,
132
+ -- We always trace this when called by `initialize_simps_projections`,
133
+ -- because this doesn't do anything extra (so should not occur in mathlib).
134
+ -- It can be useful to find the projection names.
135
+ when (is_trace_enabled_for `simps .verbose || trace_if_exists) trace!
136
+ " [simps] > Already found projection information for structure {str}:\n > {to_print}" ,
137
+ return data
114
138
else do
115
139
when_tracing `simps .verbose trace!
116
140
" [simps] > generating projection information for structure {str}:" ,
@@ -172,10 +196,17 @@ Expected type:\n {raw_expr_type}" },
172
196
when_tracing `simps .verbose trace!
173
197
" > using {proj_nm} instead of the default projection {relevant_proj.last}." ,
174
198
return $ raw_exprs.update_nth pos lambda_raw_expr) <|> return raw_exprs) raw_exprs,
175
- when_tracing `simps .verbose trace!
176
- " [simps] > generated projections for {str}:\n > {raw_exprs}" ,
177
- simps_str_attr.set str (raw_univs, raw_exprs) tt,
178
- return (raw_univs, raw_exprs)
199
+ proj_names ← e.structure_fields str,
200
+ -- if we find the name in name_changes, change the name
201
+ let proj_names : list name := proj_names.map $
202
+ λ nm, (name_changes.find $ λ p : _ × _, p.1 = nm).elim nm prod.snd,
203
+ let projs := proj_names.zip raw_exprs,
204
+ when_tracing `simps .verbose $ do {
205
+ to_print ← projs.mmap $ λ s, to_string <$> pformat!" Projection {s.1}: {s.2}" ,
206
+ let to_print := string.join $ to_print.intersperse " \n > " ,
207
+ trace!" [simps] > generated projections for {str}:\n > {to_print}" },
208
+ simps_str_attr.set str (raw_univs, projs) tt,
209
+ return (raw_univs, projs)
179
210
180
211
/--
181
212
You can specify custom projections for the `@[simps]` attribute.
@@ -194,39 +225,65 @@ Expected type:\n {raw_expr_type}" },
194
225
library_note " custom simps projection"
195
226
196
227
/-- Specify simps projections, see Note [custom simps projection].
197
- Set `trace.simps.verbose` to true to see the generated projections. -/
228
+ You can specify custom names by writing e.g.
229
+ `initialize_simps_projections equiv (to_fun → apply, inv_fun → symm_apply)`.
230
+ Set `trace.simps.verbose` to true to see the generated projections.
231
+ If the projections were already specified before, you can call `initialize_simps_projections`
232
+ again to see the generated projections. -/
198
233
@[user_command] meta def initialize_simps_projections_cmd
199
234
(_ : parse $ tk " initialize_simps_projections" ) : parser unit := do
200
235
env ← get_env,
201
- ns ← many ident,
202
- ns.mmap' $ λ nm, do nm ← resolve_constant nm, simps_get_raw_projections env nm
236
+ ns ← (prod.mk <$> ident <*> (tk " (" >>
237
+ sep_by (tk " ," ) (prod.mk <$> ident <*> (tk " ->" >> ident)) <* tk " )" )?)*,
238
+ ns.mmap' $ λ data, do
239
+ nm ← resolve_constant data.1 ,
240
+ simps_get_raw_projections env nm tt $ data.2 .get_or_else []
203
241
204
242
/--
205
243
Get the projections of a structure used by `@[simps]` applied to the appropriate arguments.
206
- Returns a list of triples (projection expression, projection name, corresponding right-hand-side),
207
- one for each projection.
244
+ Returns a list of quadruples
245
+ (projection expression, given projection name, original (full) projection name,
246
+ corresponding right-hand-side),
247
+ one for each projection. The given projection name is the name for the projection used by the user
248
+ used to generate (and parse) projection names. The original projection name is the actual
249
+ projection name in the structure, which is only used to check whether the expression is an
250
+ eta-expansion of some other expression. For example, in the structure
251
+
252
+ Example 1: ``simps_get_projection_exprs env `(α × β) `(⟨x, y⟩)`` will give the output
253
+ ```
254
+ [(`(@prod.fst.{u v} α β), `fst, `prod.fst, `(x)),
255
+ (`(@prod.snd.{u v} α β), `snd, `prod.snd, `(y))]
256
+ ```
208
257
209
- Example: ``simps_get_projection_exprs env `(α × β) `(⟨x, y⟩)`` will give the output
258
+ Example 2: ``simps_get_projection_exprs env `(α ≃ α) `(⟨id, id, λ _, rfl, λ _, rfl⟩)``
259
+ will give the output
210
260
```
211
- [(`(@prod.fst.{u v} α β), `prod.fst, `(x)), (`(@prod.snd.{u v} α β), `prod.snd, `(y))]
261
+ [(`(@equiv.to_fun.{u u} α α), `apply, `equiv.to_fun, `(id)),
262
+ (`(@equiv.inv_fun.{u u} α α), `symm_apply, `equiv.inv_fun, `(id)),
263
+ ...,
264
+ ...]
212
265
```
266
+ The last two fields of the list correspond to the propositional fields of the structure,
267
+ and are rarely/never used.
213
268
-/
214
269
-- This function does not use `tactic.mk_app` or `tactic.mk_mapp`, because the the given arguments
215
270
-- might not uniquely specify the universe levels yet.
216
271
meta def simps_get_projection_exprs (e : environment) (tgt : expr)
217
- (rhs : expr) : tactic $ list $ expr × name × expr := do
272
+ (rhs : expr) : tactic $ list $ expr × name × name × expr := do
218
273
let params := get_app_args tgt, -- the parameters of the structure
219
274
(params.zip $ (get_app_args rhs).take params.length).mmap' (λ ⟨a, b⟩, is_def_eq a b)
220
275
<|> fail " unreachable code (1)" ,
221
276
let str := tgt.get_app_fn.const_name,
222
- projs ← e.structure_fields_full str,
223
277
let rhs_args := (get_app_args rhs).drop params.length, -- the fields of the object
224
- guard (rhs_args.length = projs.length) <|> fail " unreachable code (2) " ,
225
- (raw_univs, raw_exprs) ← simps_get_raw_projections e str ,
278
+ (raw_univs, projs_and_raw_exprs) ← simps_get_raw_projections e str ,
279
+ guard (rhs_args.length = projs_and_raw_exprs.length) <|> fail " unreachable code (2) " ,
226
280
let univs := raw_univs.zip tgt.get_app_fn.univ_levels,
281
+ let projs := projs_and_raw_exprs.map $ prod.fst,
282
+ original_projection_names ← e.structure_fields_full str,
283
+ let raw_exprs := projs_and_raw_exprs.map $ prod.snd,
227
284
let proj_exprs := raw_exprs.map $
228
285
λ raw_expr, (raw_expr.instantiate_univ_params univs).instantiate_lambdas_or_apps params,
229
- return $ proj_exprs.zip $ projs.zip rhs_args
286
+ return $ proj_exprs.zip $ projs.zip $ original_projection_names.zip rhs_args
230
287
231
288
/--
232
289
Configuration options for the `@[simps]` attribute.
@@ -316,8 +373,8 @@ meta def simps_add_projections : ∀(e : environment) (nm : name) (suffix : stri
316
373
if is_constant_of (get_app_fn rhs_ap) intro then do -- if the value is a constructor application
317
374
tuples ← simps_get_projection_exprs e tgt rhs_ap,
318
375
let projs := tuples.map $ λ x, x.2 .1 ,
319
- let pairs := tuples.map $ λ x, x.2 ,
320
- eta ← expr .is_eta_expansion_aux rhs_ap pairs, -- check whether `rhs_ap` is an eta-expansion
376
+ let pairs := tuples.map $ λ x, x.2 . 2 ,
377
+ eta ← rhs_ap .is_eta_expansion_aux pairs, -- check whether `rhs_ap` is an eta-expansion
321
378
let rhs_ap := eta.lhoare rhs_ap, -- eta-reduce `rhs_ap`
322
379
/- As a special case, we want to automatically generate the current projection if `rhs_ap`
323
380
was an eta-expansion. Also, when this was a desired projection, we need to generate the
@@ -335,8 +392,14 @@ meta def simps_add_projections : ∀(e : environment) (nm : name) (suffix : stri
335
392
let x := (todo.find $ λ x, projs.all $ λ proj, ¬ (" _" ++ proj.last).is_prefix_of x).iget,
336
393
simp_lemma := nm.append_suffix $ suffix ++ x,
337
394
needed_proj := (x.split_on '_' ).tail.head in
338
- fail!" Invalid simp-lemma {simp_lemma}. Projection {needed_proj} doesn't exist." ,
339
- tuples.mmap' $ λ ⟨proj_expr, proj, new_rhs⟩, do
395
+ fail!
396
+ " Invalid simp-lemma {simp_lemma}. Structure {str} does not have projection {needed_proj}.
397
+ The known projections are:
398
+ {projs}
399
+ You can also see this information by running
400
+ `initialize_simps_projections {str}`.
401
+ Note: the projection names used by @[simps] might not correspond to the projection names in the structure." ,
402
+ tuples.mmap' $ λ ⟨proj_expr, proj, _, new_rhs⟩, do
340
403
new_type ← infer_type new_rhs,
341
404
let new_todo := todo.filter_map $ λ x, string.get_rest x $ " _" ++ proj.last,
342
405
b ← is_prop new_type,
@@ -380,7 +443,7 @@ meta def simps_tac (nm : name) (cfg : simps_cfg := {}) (todo : list string := []
380
443
381
444
/-- The parser for the `@[simps]` attribute. -/
382
445
meta def simps_parser : parser (list string × simps_cfg) := do
383
- /- note: we currently don't check whether the user has written a nonsense namespace as arguments . -/
446
+ /- note: we don't check whether the user has written a nonsense namespace in an argument . -/
384
447
prod.mk <$> many (name.last <$> ident) <*>
385
448
(do some e ← parser.pexpr? | return {}, eval_pexpr simps_cfg e)
386
449
@@ -420,10 +483,14 @@ derives two simp-lemmas:
420
483
⇑((e₁.trans e₂).symm) a = (⇑(e₁.symm) ∘ ⇑(e₂.symm)) a
421
484
```
422
485
486
+ * You can specify custom projection names, by specifying the new projection names using
487
+ `initialize_simps_projections`.
488
+ Example: `initialize_simps_projections equiv (to_fun → apply, inv_fun → symm_apply)`.
489
+
423
490
* If one of the fields itself is a structure, this command will recursively create
424
491
simp-lemmas for all fields in that structure.
425
492
* Exception: by default it will not recursively create simp-lemmas for fields in the structures
426
- `prod` and `pprod`. Give explicit projection names to override this.
493
+ `prod` and `pprod`. Give explicit projection names to override this behavior .
427
494
428
495
Example:
429
496
```lean
@@ -432,7 +499,7 @@ derives two simp-lemmas:
432
499
```
433
500
generates
434
501
```lean
435
- @[ simp ] lemma foo_fst : foo.fst = (1, 2)
502
+ @[ simp ] lemma foo_fst : foo.fst = (1, 2)
436
503
@[ simp ] lemma foo_snd_fst : foo.snd.fst = 3
437
504
@[ simp ] lemma foo_snd_snd : foo.snd.snd = 4
438
505
```
0 commit comments