Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 3287c94

Browse files
committed
refactor(tactic/ext): simplify ext attribute (#8785)
This simplifies the `ext` attribute from taking a list with `*`, `(->)` and names to just `@[ext]` or `@[ext ident]`. The `(->)` option is only used once, in the file that declares the `ext` attribute itself, so it's not worth the parser complexity. The ability to remove attributes with `@[ext -foo]` is removed, but I don't think it was tested because it was never used and doesn't work anyway. Also fixes an issue with ext attributes being quadratic (in the number of ext attributes applied), and also makes `ext` attributes remove themselves (the real work of ext attributes is done by two internal attributes `_ext_core` and `_ext_lemma_core`), so that they don't pollute `#print` output. Before: ```lean #print funext @[_ext_lemma_core, ext list.cons.{0} ext_param_type (sum.inr.{0 0} (option.{0} name) (option.{0} name) (option.some.{0} name (name.mk_numeral (unsigned.of_nat' (has_zero.zero.{0} nat nat.has_zero)) name.anonymous))) (list.cons.{0} ext_param_type (sum.inr.{0 0} (option.{0} name) (option.{0} name) (option.some.{0} name (name.mk_string "thunk" name.anonymous))) (list.nil.{0} ext_param_type)), intro!] theorem funext : ∀ {α : Sort u} {β : α → Sort v} {f₁ f₂ : Π (x : α), β x}, (∀ (x : α), f₁ x = f₂ x) → f₁ = f₂ := ``` After: ```lean #print funext @[_ext_lemma_core, intro!] theorem funext : ∀ {α : Sort u} {β : α → Sort v} {f₁ f₂ : Π (x : α), β x}, (∀ (x : α), f₁ x = f₂ x) → f₁ = f₂ := ```
1 parent a4b33d3 commit 3287c94

File tree

1 file changed

+34
-55
lines changed

1 file changed

+34
-55
lines changed

src/tactic/ext.lean

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,6 @@ meta def get_ext_subject : expr → tactic name
127127

128128
open native
129129

130-
@[reducible] def ext_param_type := option name ⊕ option name
131-
132-
meta def opt_minus : lean.parser (option name → ext_param_type) :=
133-
sum.inl <$ tk "-" <|> pure sum.inr
134-
135-
meta def ext_param :=
136-
opt_minus <*> ( name.mk_numeral 0 name.anonymous <$ brackets "(" ")" (tk "" <|> tk "->") <|>
137-
none <$ tk "*" <|>
138-
some <$> ident )
139-
140130
meta def saturate_fun : name → tactic expr
141131
| (name.mk_numeral 0 name.anonymous) :=
142132
do v₀ ← mk_mvar,
@@ -180,7 +170,7 @@ library_note "user attribute parameters"
180170
For performance reasons, the parameters of the `@[ext]` attribute are stored
181171
in two auxiliary attributes:
182172
```lean
183-
attribute [ext [thunk]] funext
173+
attribute [ext thunk] funext
184174
185175
-- is turned into
186176
attribute [_ext_core (@id name @funext)] thunk
@@ -202,11 +192,9 @@ private meta def ext_attr_core : user_attribute (name_map name) name :=
202192
descr := "(internal attribute used by ext)",
203193
cache_cfg := {
204194
dependencies := [],
205-
mk_cache := λ ns, do
206-
attrs ← ns.mmap (λ n, do
207-
ext_l ← ext_attr_core.get_param_untyped n,
208-
pure (n, ext_l.app_arg.const_name)),
209-
pure $ rb_map.of_list attrs },
195+
mk_cache := λ ns, ns.mfoldl (λ m n, do
196+
ext_l ← ext_attr_core.get_param_untyped n,
197+
pure (m.insert n ext_l.app_arg.const_name)) mk_name_map },
210198
parser := failure }
211199

212200
end performance_hack
@@ -231,6 +219,11 @@ Returns the extensionality lemmas in the environment, as a list of lemma names.
231219
meta def get_ext_lemma_names : tactic (list name) :=
232220
attribute.get_instances ext_lemma_attr_core.name
233221

222+
/-- Marks `lem` as an extensionality lemma corresponding to type constructor `constr`;
223+
if `persistent` is true then this is a global attribute, else local. -/
224+
meta def add_ext_lemma (constr lem : name) (persistent : bool) : tactic unit :=
225+
ext_attr_core.set constr lem persistent >> ext_lemma_attr_core.set lem () persistent
226+
234227
/--
235228
Tag lemmas of the form:
236229
@@ -247,23 +240,7 @@ the lemma. In some cases, the same lemma can be used to state the
247240
extensionality of multiple types that are definitionally equivalent.
248241
249242
```lean
250-
attribute [ext [(→),thunk,stream]] funext
251-
```
252-
253-
Those parameters are cumulative. The following are equivalent:
254-
255-
```lean
256-
attribute [ext [(→),thunk]] funext
257-
attribute [ext [stream]] funext
258-
```
259-
and
260-
```lean
261-
attribute [ext [(→),thunk,stream]] funext
262-
```
263-
264-
One removes type names from the list for one lemma with:
265-
```lean
266-
attribute [ext [-stream,-thunk]] funext
243+
attribute [ext thunk, ext stream] funext
267244
```
268245
269246
Also, the following:
@@ -278,7 +255,7 @@ lemma my_collection.ext (a b : my_collection)
278255
is equivalent to
279256
280257
```lean
281-
@[ext *]
258+
@[ext my_collection]
282259
lemma my_collection.ext (a b : my_collection)
283260
(h : ∀ x, a.lookup x = b.lookup y) :
284261
a = b := ...
@@ -288,7 +265,7 @@ This allows us specify type synonyms along with the type
288265
that is referred to in the lemma statement.
289266
290267
```lean
291-
@[ext [*,my_type_synonym]]
268+
@[ext, ext my_type_synonym]
292269
lemma my_collection.ext (a b : my_collection)
293270
(h : ∀ x, a.lookup x = b.lookup y) :
294271
a = b := ...
@@ -316,27 +293,23 @@ x = y ↔ x.x = y.x ∧ x.y = y.y ∧ x.z == y.z ∧ x.k = y.k
316293
317294
-/
318295
@[user_attribute]
319-
meta def extensional_attribute : user_attribute unit (list ext_param_type) :=
296+
meta def extensional_attribute : user_attribute unit (option name) :=
320297
{ name := `ext,
321298
descr := "lemmas usable by `ext` tactic",
322-
parser := pure <$> ext_param <|> list_of ext_param <|> pure [],
323-
after_set := some $ λ n prio b,
324-
do ls ← extensional_attribute.get_param n,
325-
e ← get_env,
326-
n ← if (e.structure_fields n).is_some
327-
then derive_struct_ext_lemma n
328-
else pure n,
329-
s ← mk_const n >>= infer_type >>= get_ext_subject,
330-
let (rs,ls'') := if ls.empty
331-
then ([],[s])
332-
else ls.partition_map (sum.map (flip option.get_or_else s)
333-
(flip option.get_or_else s)),
334-
ls''.mmap' (equiv_type_constr s),
335-
ls' ← get_ext_lemmas,
336-
let l := ls'' ∪ (ls'.to_list.filter $ λ l, prod.snd l = n).map prod.fst \ rs,
337-
l.mmap' $ λ l, do
338-
ext_attr_core.set l n b prio,
339-
ext_lemma_attr_core.set n () b prio }
299+
parser := optional ident,
300+
before_unset := some $ λ _ _, pure (),
301+
after_set := some $ λ n _ b, do
302+
add ← extensional_attribute.get_param n,
303+
unset_attribute `ext n,
304+
e ← get_env,
305+
n ← if (e.structure_fields n).is_some
306+
then derive_struct_ext_lemma n
307+
else pure n,
308+
s ← mk_const n >>= infer_type >>= get_ext_subject,
309+
match add with
310+
| none := add_ext_lemma s n b
311+
| some add := equiv_type_constr s add >> add_ext_lemma add n b
312+
end }
340313

341314
add_tactic_doc
342315
{ name := "ext",
@@ -361,7 +334,13 @@ library_note "partially-applied ext lemmas"
361334

362335
-- We mark some existing extensionality lemmas.
363336
attribute [ext] array.ext propext function.hfunext
364-
attribute [ext [(→),thunk]] _root_.funext
337+
attribute [ext thunk] _root_.funext
338+
339+
-- This line is equivalent to:
340+
-- attribute [ext (→)] _root_.funext
341+
-- but (→) is not actually a binary relation with a constant at the head,
342+
-- so we use the special name [anon].0 to represent (→).
343+
run_cmd add_ext_lemma (name.mk_numeral 0 name.anonymous) ``_root_.funext tt
365344

366345
-- We create some extensionality lemmas for existing structures.
367346
attribute [ext] ulift

0 commit comments

Comments
 (0)