Zulip Chat Archive
Stream: mathlib4
Topic: local attribute [- ...]
Heather Macbeth (Jan 03 2023 at 05:11):
Is there a way of spelling local attribute [-...]
in Lean 4? I used to be able to do
import tactic.norm_num
local attribute [-norm_num] norm_num.eval_nat_int_ext
to disable a norm_num
extension, this doesn't seem to work in mathlib 4 or maybe I have the wrong syntax?
import Mathlib.Tactic.NormNum.Basic
attribute [-norm_num] Mathlib.Meta.NormNum.evalPow
-- attribute cannot be erased
Heather Macbeth (Jan 03 2023 at 05:11):
That one's for teaching, but there are also a number of uses of local attribute [-instance]
in mathlib.
Mario Carneiro (Jan 03 2023 at 05:24):
This seems to be spelled attribute [-foo]
now; erasing is only supported locally so there is no syntax like attribute [local -foo]
Mario Carneiro (Jan 03 2023 at 05:25):
the reason it doesn't work for norm_num
is because attributes have to explicitly opt-in to being erased and you have to write some extra code to support it. Most mathlib4 tactics don't support erasing right now
Heather Macbeth (Jan 03 2023 at 05:29):
Ah great. Indeed instance erasing works just fine (I guess the fact that it was local
automatically is what I hadn't realised):
import Mathlib.Data.Int.Order.Basic
example {a : ℤ} {b : ℤ} (h1 : 0 ≤ a) (h2 : 0 ≤ b) : a < b ↔ a * a < b * b :=
mul_self_lt_mul_self_iff h1 h2 -- works
attribute [-instance] Int.linearOrderedCommRing
example {a : ℤ} {b : ℤ} (h1 : 0 ≤ a) (h2 : 0 ≤ b) : a < b ↔ a * a < b * b :=
mul_self_lt_mul_self_iff h1 h2 -- fails
Heather Macbeth (Jan 03 2023 at 05:30):
How hard would it be to make norm_num
erasable? Is there any similar example already implemented that could serve as a model?
Heather Macbeth (Jan 03 2023 at 05:31):
Do you mind if I add this as an issue? (If there's no reason in principle why it couldn't be done?)
Mario Carneiro (Jan 03 2023 at 06:34):
Heather Macbeth said:
Do you mind if I add this as an issue? (If there's no reason in principle why it couldn't be done?)
Yes please, there is no fundamental issue with implementation. A similar example might be simp
: you keep a map for erased norm_num extensions in addition to the regular map, and if the regular map produces a result, we first check the erased map to make sure the extension has not been erased, and if it is we skip it
Heather Macbeth (Jan 03 2023 at 08:21):
mathlib4#1308, feel free to make any corrections.
Thomas Murrills (Jan 06 2023 at 01:37):
@Sarah Smith reached out to me about trying this—I'm going to give it a shot, if that's alright :) I'll open a branch + PR for it and start committing!
Thomas Murrills (Jan 06 2023 at 06:43):
If anyone has some good simple examples of norm_num
erasure (cases where something can't be proved when something is erased) for the test file, they're welcome here :)
Thomas Murrills (Jan 06 2023 at 06:46):
Also, for anyone familiar with the existing code: is there a reason we don't keep track of the Entry
list when we use addImportedFn
? Currently it returns pure ⟨[], dt⟩
.
I'm a little unsure of when we actually use the list of Entry
s, to be honest...
Heather Macbeth (Jan 06 2023 at 06:47):
Here's one:
example : 3 ^ 3 + 4 = 31 := by
norm_num1
with_reducible rfl
fails if you put it before the evalPow
extension, succeds if you put it after.
Heather Macbeth (Jan 06 2023 at 06:48):
So you'd like it to fail if you disable the evalPow
extension.
Thomas Murrills (Jan 06 2023 at 07:06):
It works! :) Up to the above details about the Entry
list.
Thomas Murrills (Jan 06 2023 at 07:07):
Is there a way to restore an attribute after erasing it? I would have expected
attribute [norm_num] Mathlib.Meta.NormNum.evalPow
but that doesn't work.
Heather Macbeth (Jan 06 2023 at 07:10):
Can you mark the erasure with
attribute [-norm_num] in
lemma ....
to make it local to that lemma?
Heather Macbeth (Jan 06 2023 at 07:10):
Or enclose the erasure in section
/end
?
Thomas Murrills (Jan 06 2023 at 07:12):
Hmmm...I would have expected both of those to work, but neither of them do.
Thomas Murrills (Jan 06 2023 at 07:13):
Interestingly it doesn't complain about attribute [-norm_num] ... in
, but nonetheless it doesn't work as expected. I wonder if that's something I can fix inside norm_num
or if it has to do with how [-_]
is elaborated...
Heather Macbeth (Jan 06 2023 at 07:18):
I have no idea, hopefully an expert will comment.
Mario Carneiro (Jan 06 2023 at 07:51):
Thomas Murrills said:
Also, for anyone familiar with the existing code: is there a reason we don't keep track of the
Entry
list when we useaddImportedFn
? Currently it returnspure ⟨[], dt⟩
.I'm a little unsure of when we actually use the list of
Entry
s, to be honest...
addImportedFn
is responsible for setting up the initial state of the attribute after importing upstream modules (i.e. just after all the import
lines). The first component of the pair is the list of entries defined in the current file, so it starts out empty. The list component is pushed to in addEntryFn
, which is called when a new entry is added in the current file, and the list of entries is serialized into an array for future import
s in exportEntriesFn
Mario Carneiro (Jan 06 2023 at 07:54):
Thomas Murrills said:
Is there a way to restore an attribute after erasing it? I would have expected
attribute [norm_num] Mathlib.Meta.NormNum.evalPow
but that doesn't work.
No, the only way to restore an erased attribute is to do it in a section and pop the scope
Thomas Murrills (Jan 06 2023 at 08:15):
Gotcha, makes sense!
Reid Barton (Jan 06 2023 at 08:16):
BTW is "erased" the official word for this? It has an unrelated, more common meaning in theorem proving/programming...
Thomas Murrills (Jan 06 2023 at 08:19):
I need a way to check if a name has [norm_num]
, though. I can't simply check if it's NormNumExt
, because that doesn't account for already-erased attributes. The three options I can see are
- get all
NormNumExt
nodes of theDiscrTree
(yikes?) - add a field to the state that keeps track of active
norm_nums
- check if the name is
NormNumExt
by looking at the environment and check if not alreadyerased
Which of these is best (or should I do something else)?
Thomas Murrills (Jan 06 2023 at 08:20):
Reid Barton said:
BTW is "erased" the official word for this? It has an unrelated, more common meaning in theorem proving/programming...
Yep: -
in e.g. [-norm_num]
is secretly an eraseAttr
syntax node, and the functionality for erasing an attribute is given by including the field erase
when registering it :)
Mario Carneiro (Jan 06 2023 at 08:42):
I'm okay with saying that the usage of the word "erase" in this context is dev-only though
Mario Carneiro (Jan 06 2023 at 08:43):
not that I have a better naming suggestion
Mario Carneiro (Jan 06 2023 at 08:44):
@Thomas Murrills in what context do you need to know if a name is @[norm_num]
tagged?
Thomas Murrills (Jan 06 2023 at 08:44):
In defining the function erase
Mario Carneiro (Jan 06 2023 at 08:44):
to give an error message if it's not already tagged?
Thomas Murrills (Jan 06 2023 at 08:44):
It should throw an error if the name doesn't have [norm_num]
, yeah
Thomas Murrills (Jan 06 2023 at 08:45):
At least, patterning off existing erase
s
Mario Carneiro (Jan 06 2023 at 08:45):
what does simp
do?
Thomas Murrills (Jan 06 2023 at 08:46):
simp
carries around all of the lemma names it's using in the state, I think?
Thomas Murrills (Jan 06 2023 at 08:46):
My guess is that it has other reasons to do so, though
Mario Carneiro (Jan 06 2023 at 08:47):
oh I see, it's the lemmaNames
field
Mario Carneiro (Jan 06 2023 at 08:47):
nope, it is used nowhere else
Mario Carneiro (Jan 06 2023 at 08:47):
except in a test
Thomas Murrills (Jan 06 2023 at 08:49):
oh! well how about that
Thomas Murrills (Jan 06 2023 at 08:49):
but would (3) work? it seems more lightweight
Thomas Murrills (Jan 06 2023 at 08:49):
plus we need to access the environment in the course of giving the erase
field anyway, so it's available
Mario Carneiro (Jan 06 2023 at 08:51):
being a NormNumExt
is technically unrelated to being a norm num extension, you could always just define a random def with that type
Mario Carneiro (Jan 06 2023 at 08:52):
If you want to avoid the extra state I would prefer (1)
Mario Carneiro (Jan 06 2023 at 08:53):
if it's a rare operation then a linear time operation over the discr tree seems fine, there is a small finite number of norm num extensions anyway
Thomas Murrills (Jan 06 2023 at 08:54):
Ok, nice; do you happen to know of a function to get all such nodes out of the DiscrTree
or should I write it? I couldn't see it in DiscrTree.lean
.
Thomas Murrills (Jan 06 2023 at 08:57):
(Also just to be clear the only reason I want to err on the side of avoiding the extra state is performance, which I don't have great intuition for—if it's fine to add to the state, that's probably easier, tbh.)
Mario Carneiro (Jan 06 2023 at 08:59):
Jannis had some DiscrTree operations in a PR to std I think
Mario Carneiro (Jan 06 2023 at 09:01):
Mario Carneiro (Jan 06 2023 at 09:01):
you can steal the operations out of that and we can clean it up later
Thomas Murrills (Jan 06 2023 at 09:05):
sounds good! :) I'll leave a note.
Thomas Murrills (Jan 06 2023 at 21:51):
Mario Carneiro said:
Thomas Murrills said:
Is there a way to restore an attribute after erasing it? I would have expected
attribute [norm_num] Mathlib.Meta.NormNum.evalPow
but that doesn't work.
No, the only way to restore an erased attribute is to do it in a section and pop the scope
How exactly do you spell the scope pop? I was expecting maybe
namespace norm_num_erase
scoped[norm_num_erase] attribute [-norm_num] ...
but that doesn't seem to be right.
Mario Carneiro (Jan 07 2023 at 00:32):
section stuff
attribute [-norm_num] Mathlib.Meta.NormNum.evalPow
...
end stuff
-- Mathlib.Meta.NormNum.evalPow is not erased
Mario Carneiro (Jan 07 2023 at 00:33):
attribute [-norm_num] Mathlib.Meta.NormNum.evalPow in
example : bla := ...
should have the same effect, if you just want to erase the attribute in one declaration
Thomas Murrills (Jan 07 2023 at 00:53):
Those are what @Heather Macbeth originally suggested, but neither work for some reason
section stuff
attribute [-norm_num] Mathlib.Meta.NormNum.evalPow
end stuff
example : 3 ^ 3 + 4 = 31 := by
norm_num1
guard_target =ₛ 3 ^ 3 + 4 = 31 -- no change; still erased
rfl
Thomas Murrills (Jan 07 2023 at 00:55):
attribute [-norm_num] Mathlib.Meta.NormNum.evalPow in
example := true
example : 3 ^ 3 + 4 = 31 := by
norm_num1
guard_target =ₛ 3 ^ 3 + 4 = 31 -- no change; still erased
rfl
Mario Carneiro (Jan 07 2023 at 01:24):
It could be an issue in how you implemented the erase function? That is how erasing attributes is supposed to work from the user side
Thomas Murrills (Jan 07 2023 at 01:41):
Maybe—I tried to pattern it pretty closely after existing ones, but I’ll poke around and see if I can figure it out.
Thomas Murrills (Jan 07 2023 at 01:55):
Update: really weird (to me): when I try to erase an imported norm_num
, it works, but exhibits the above bad behavior. When i try to erase a norm_num
extension defined in the same file, I get the error attribute cannot be erased
—apparently AttributeImpl.erase
is being called.
Thomas Murrills (Jan 07 2023 at 02:30):
My guess currently is that e.g. simp
uses a ScopedEnvExtension
whereas norm_num
just uses a PersistentEnvExtension
?
Mario Carneiro (Jan 07 2023 at 02:31):
A ScopedEnvExtension
is just a PersistentEnvExtension
with extra logic that has been inlined into the norm_num extension
Thomas Murrills (Jan 07 2023 at 02:33):
...Which I guess means that erase
needs that extra logic to be inlined as well in order to work correctly? Or am I misinterpreting
Mario Carneiro (Jan 07 2023 at 02:33):
yes, most likely
Thomas Murrills (Jan 07 2023 at 02:34):
Ok, thanks, that's helpful...I think I know where to look now
Thomas Murrills (Jan 07 2023 at 04:47):
Hmm, I'm not totally sure the norm_num attribute does inline that extra logic yet. ScopedEnvExtension
uses a StateStack
which comes with pushScope
and popScope
functionality, but I don't see any circumlocutions for the state stack and scopes etc. in norm_num
.
Thomas Murrills (Jan 07 2023 at 04:47):
Am I correct in thinking that it's these functions which are responsible for managing the relevant scopes when e.g. section
/end
occurs?
Mario Carneiro (Jan 07 2023 at 04:47):
yes
Thomas Murrills (Jan 07 2023 at 04:52):
Cool. Given that the extra logic doesn't exist yet, is it still a good idea to inline the necessary logic, or is it better to just switch to a ScopedEnvExtension
?
Thomas Murrills (Jan 07 2023 at 04:52):
still not sure what's going on with AttributeImpl.erase
, though, and I wonder if it would be magically solved by using a ScopedEnvExtension
.
Mario Carneiro (Jan 07 2023 at 04:53):
I saw something about calling .erase
on the list of erased attributes on your PR, which seems wrong
Thomas Murrills (Jan 07 2023 at 04:55):
That's when a norm_num extension is added (to make sure it's no longer erased). Not sure it really would ever apply, though.
Thomas Murrills (Jan 07 2023 at 04:56):
Also it's just the hash map .erase
, not the attribute's erase
Thomas Murrills (Jan 07 2023 at 04:57):
It's patterned off of what SimpTheorems.addConst
does
Mario Carneiro (Jan 07 2023 at 05:03):
that should never happen because you can't add the norm_num attribute to a definition twice
Thomas Murrills (Jan 07 2023 at 05:05):
Yeah, that's what I thought, but I didn't want to fail to account for some edge case I couldn't anticipate
Thomas Murrills (Jan 07 2023 at 05:05):
I mean, it doesn't have anything to do with solving this scoping problem, though, right?
Mario Carneiro (Jan 07 2023 at 05:06):
I thought you were trying to do scopes that way
Thomas Murrills (Jan 07 2023 at 05:07):
I don't follow—which way?
Mario Carneiro (Jan 07 2023 at 05:07):
by erasing things from the map
Mario Carneiro (Jan 07 2023 at 05:07):
which is wrong and explains the issues you were having earlier
Thomas Murrills (Jan 07 2023 at 05:08):
Currently I don't do scopes, is the point, I think.
Mario Carneiro (Jan 07 2023 at 05:09):
to answer your question from before, using ScopedEnvExtension
would make sense; the reason I avoided it before was because we didn't need scopes
Thomas Murrills (Jan 07 2023 at 05:10):
Would it make sense to implement scopes just for the hash set erased
in the state somehow, without scoping the whole state?
Mario Carneiro (Jan 07 2023 at 05:10):
if there is a way to implement scopes in a more lightweight way which takes advantage of the fact that norm num erasure is very rare and @[norm_num]
itself can't be scoped, then that would be good too
Thomas Murrills (Jan 07 2023 at 05:11):
Hmm. Can I inspect the current scope?
Thomas Murrills (Jan 07 2023 at 05:11):
From within IO Unit
Mario Carneiro (Jan 07 2023 at 05:11):
no
Mario Carneiro (Jan 07 2023 at 05:12):
certainly not just with that
Mario Carneiro (Jan 07 2023 at 05:12):
all of lean's state is up in some monad's Context
or State
type
Thomas Murrills (Jan 07 2023 at 05:13):
Right, I noticed it in e.g. CommandElabM
's state, iirc—I wasn't sure if IO
extended it or something (or not)
Mario Carneiro (Jan 07 2023 at 05:13):
no it's the other way around, IO
is at the very bottom of the monad hierarchy
Thomas Murrills (Jan 07 2023 at 05:14):
ahh
Thomas Murrills (Jan 07 2023 at 05:14):
Hmm. erase
operates in AttrM
. Is that enough?
Thomas Murrills (Jan 07 2023 at 05:14):
Wait
Thomas Murrills (Jan 07 2023 at 05:14):
Let me read that type again
Mario Carneiro (Jan 07 2023 at 05:14):
AttrM is CoreM, which has a decent amount of state
Mario Carneiro (Jan 07 2023 at 05:15):
but I think you are asking the wrong question
Mario Carneiro (Jan 07 2023 at 05:15):
all of an extension's state has to live inside the EnvExtension
Mario Carneiro (Jan 07 2023 at 05:16):
so if your attribute has a scoping effect then it needs to have a scope stack of some kind
Thomas Murrills (Jan 07 2023 at 05:16):
Ok, makes sense
Thomas Murrills (Jan 07 2023 at 05:16):
But what sort of things can implement that?
Mario Carneiro (Jan 07 2023 at 05:17):
what do you mean?
Thomas Murrills (Jan 07 2023 at 05:17):
How can I get the scope state into the EnvExtension
state?
Thomas Murrills (Jan 07 2023 at 05:17):
Or, is that not quite how things flow?
Mario Carneiro (Jan 07 2023 at 05:17):
it's the type variables in PersistentEnvExtension
Mario Carneiro (Jan 07 2023 at 05:17):
(see the doc comment too: docs4#Lean.PersistentEnvExtension)
Thomas Murrills (Jan 07 2023 at 05:21):
Right, ok...so, hmm. Let's say a section
or end
is encountered. some pushScope
or popScope
occurs, right? Is that meant to reach in and affect the state somehow?
Mario Carneiro (Jan 07 2023 at 05:21):
hm, looks like ScopedEnvExtension
s do have an extra bit of magic: src4#Lean.pushScope
Thomas Murrills (Jan 07 2023 at 05:22):
I saw that! I was wondering how that worked
Mario Carneiro (Jan 07 2023 at 05:22):
section
calls that function, which checks a global list of all ScopedEnvExtension
s which is pushed to in registerScopedEnvExtension
Mario Carneiro (Jan 07 2023 at 05:23):
so that means that even if you make a scoping-like PersistentEnvExtension
you won't get a callback so you can't detect scopes
Thomas Murrills (Jan 07 2023 at 05:23):
I seee
Thomas Murrills (Jan 07 2023 at 05:28):
Hmmm. So is the best option really to turn the whole thing into a ScopedEnvExtension
? Or is there some tricky way we could, I don't know, set up a separate scoped extension just for keeping track of erased norm_nums, which the PersistentEnvExtension
can keep a reference to? I'm not sure if that's possible, though. It seems like the persistent state can't access any of the information needed.
Thomas Murrills (Jan 07 2023 at 05:31):
Unless—could the auxiliary scoped extension reach in and update a field of the persistent extension when it encountered a new scope?
Mario Carneiro (Jan 07 2023 at 05:32):
you still have a fair amount of control with a scoped env extension, the interface is a little different though
Mario Carneiro (Jan 07 2023 at 05:32):
I don't think it should be a problem to scope only what you want to
Thomas Murrills (Jan 07 2023 at 05:33):
Ok, neat—one thing I noticed was that addImportedFn
didn't seem to have a direct analogue type-wise
Thomas Murrills (Jan 07 2023 at 05:34):
This is in theDescr
argument to registerScopedEnvExtension
Thomas Murrills (Jan 07 2023 at 05:37):
I thought the OLean
fields might be related, but they don't quite do the same thing...
Mario Carneiro (Jan 07 2023 at 05:38):
you can see how they relate in src4#Lean.ScopedEnvExtension.addImportedFn
Thomas Murrills (Jan 07 2023 at 05:44):
Oh I see! That looks like it makes sense. Tyvm for taking the time to explain all this. I'm going to pick this back up sometime tomorrow if I can :)
Thomas Murrills (Jan 09 2023 at 21:29):
Ok, I think I got this working! mathlib4#1364
Thomas Murrills (Jan 09 2023 at 21:37):
(A bit worried about the necessary switch from two foldlM
's to ScopedEnvExtension
's nested for ... in
's when building the tree on import, but I don't know—maybe it's the same behind the scenes?)
Thomas Murrills (Jan 09 2023 at 21:40):
Also, as I'm guessing scoped extensions should be, it's now localizable—though I doubt that will get much, if any, use :)
Last updated: Dec 20 2023 at 11:08 UTC