Zulip Chat Archive

Stream: lean4

Topic: simp working only after dsimp


Thomas Murrills (Dec 17 2022 at 04:49):

In a file I'm porting from mathlib (mathlib#1088), a mathlib proof of the formext; simp worked in lean 3, but needed to be changed to ext; dsimp; simp to work now.

A notable thing here is that the goal after ext; dsimp looks exactly the same as it does with just ext; simp; you have to set_option pp.all true to see the difference (not just pp.explicit).

Is this weird enough to warrant making an mwe for?

Kevin Buzzard (Dec 17 2022 at 08:51):

In my opinion every simp call which stops working after being ported should at least be flagged with a porting note, and ideally should be investigated. dsimp, simp is strictly speaking not good style because of the nonterminal dsimp. Does dsimp only; simp work?

Yaël Dillies (Dec 17 2022 at 08:56):

Kevin, once again this is all fine.

Yaël Dillies (Dec 17 2022 at 08:56):

Fancy tactics can be followed by fancy tactics, and arguably dsimp isn't even fancy at all.

Kevin Buzzard (Dec 17 2022 at 09:03):

You've said this before and then others have said "no it's not fine", right? I'm still unclear about this.

Mario Carneiro (Dec 17 2022 at 11:35):

I recall being part of that earlier conversation, and my line is that dsimp is a fancy tactic, but fancy tactics followed by other fancy tactics is okay. (Although dsimp, simp doing something different from simp is probably a bug in any case.)

Kevin Buzzard (Dec 17 2022 at 19:37):

I was talking to Oliver about this recently. Isn't the only reason we do dsimp, simp that dsimp unfolds structure projections but simp doesn't? Oliver suggested making the structure projection unfolding a simp lemma (Amelia was having problems with dsimps taking a long time)

Thomas Murrills (May 04 2023 at 23:10):

Just want to record that I encountered this again, this time in the form of needing to dsimp only [] at hypotheses for simp [*] to use them!

example (P : α  Prop) (h : P (y,y).1) : P y := by -- unsolved goals ...
  simp only [*]

example (P : α  Prop) (h : P (y,y).1) : P y := by -- works
  dsimp only [] at h
  simp only [*]

(Note that simp only [*, h] also is insufficient to close the goal.)

Thomas Murrills (May 04 2023 at 23:11):

Is there a better workaround than writing dsimp only ..., specifically in the context of using simp inside another tactic? Possibly a config option? If not, I'm considering having the tactic run dsimp only [] at * if the user provides a simp star, though this is imperfect, since it might change hypotheses that the user wouldn't expect to be touched. (If that were a big issue, maybe I could restore all hypotheses that weren't reported as used by simp, if the goal isn't closed? Not necessary for the immediate application, though.)

Kyle Miller (May 04 2023 at 23:17):

For what it's worth, I'd probably reach for simpa using h here

Kyle Miller (May 04 2023 at 23:22):

It'd be interesting if simp [*] would run simp on each local hypothesis while creating its simp set (no need to literally simp at *, unless you're just wanting to avoid reaching into internals -- it's possible to run the simplifier on any given Expr).

Thomas Murrills (May 04 2023 at 23:22):

simpa seems to work in the example! But in general (and in the real example that prompted this) we need to use simp only [*, simp_lemma1, simp_lemma2] and interleave the hypothesis applications with lemma simps, and there it struggles.

Thomas Murrills (May 04 2023 at 23:25):

It would! (I wonder what happens currently if a hypothesis is not in simp normal form or something, and causes a loop?) Though, I'd even settle for running the internal version of dsimp only [] on each hypothesis while creating the simp set. :)

Kyle Miller (May 04 2023 at 23:25):

Sometimes the best configuration option is simply a composition of tactics. Is there anything that a configuration option would let you do that preceding simp with dsimp only at * wouldn't do?

Thomas Murrills (May 04 2023 at 23:28):

If I run dsimp only at * inside the tactic I'm writing, then the user's hypotheses would change unexpectedly, unless I manage goals and the like. Not the biggest problem in the world, but still. Though, I am using simpGoals explicitly, and generating the simp context from syntax, so maybe there's an easy way to do this "the right way" somehow?

Kyle Miller (May 04 2023 at 23:28):

(and regarding trying to put things into simp normal form, just for comparison one place that does something about it is the @[elementwise] attribute, which uses a function to simp the LHS and RHS of the generated equality lemma separately)

Kyle Miller (May 04 2023 at 23:31):

Another "lean metaprogramming koan" for you is that making your tactic less smart makes it a smarter tactic. What feature are you trying to implement that you feel needs dsimp only at *?

Kyle Miller (May 04 2023 at 23:31):

It just occurred to me that even better than simpa using h for your examples is assumption. Many tactics use assumption to discharge some obvious goals.

Kyle Miller (May 04 2023 at 23:33):

Thomas Murrills said:

If I run dsimp only at * inside the tactic I'm writing, then the user's hypotheses would change unexpectedly

I was meaning that you'd leave the dsimp only at * to the user, if possible. Though maybe your tactic makes some hypotheses that you know will need simplification before they're useful.

Thomas Murrills (May 04 2023 at 23:44):

Kyle Miller said:

Another "lean metaprogramming koan" for you is that making your tactic less smart makes it a smarter tactic. What feature are you trying to implement that you feel needs dsimp only at *?

Ah, very wise. :) So, specifically, mono* using <simpArgs> (where mono* applies mono repetitively) runs (in MetaM, not by macro) simp only [<simpArgs>] before each application of mono. One of the extant usages of mono* is of the above form: there are hypotheses in the context which, if dsimp'd, could be used simp inside this tactic invocation. Moreover, the hypotheses need to be interleaved with other provided simp lemmas—we're running mono* using *, lem1, lem2—so following it by assumption doesn't work.

So basically, I was thinking I might try to patch what I see as a bug in simp "by hand". :)

In this particular case, mono (being fancy) also relies on solve_by_elim. Maybe it would make sense to pass these simp lemmas to solve_by_elim too, though that seems like overkill. (But if that takes more than a little while I might just say it's fine for now, get mono out there, and enhance it later. :) )

Kyle Miller (May 04 2023 at 23:48):

I've wished for a feature where simp could be told to simplify local hypotheses before. As a first step, maybe (if it doesn't already exist) there could be a simp% term elaborator that simplifies a term so you could write simp [simp% h, simp% s].

Kyle Miller (May 04 2023 at 23:49):

I wonder if * in a simp lemma list is ever "correct", or merely convenient. Simp expects simp lemmas, but random hypotheses in the local context surely aren't simp lemmas since what are the chances they're in normal form?

Kyle Miller (May 04 2023 at 23:50):

That suggests that either it shouldn't be used in non-draft code, or simp should be responsible for trying to transform the lemmas drawn in using * into a simp set.

Kyle Miller (May 04 2023 at 23:51):

(That said, I use it all the time for one-liner induction arguments: by induction x <;> simp [*] when it works is nice and short. The main purpose of * here is that I wanted to avoid naming the induction hypotheses, and furthermore, each case will have different local contexts.)

Thomas Murrills (May 04 2023 at 23:55):

It does seem pretty unsafe! I just wanted to check, and indeed you can make it loop indefinitely in the obvious way:

example (P : α  Prop) (h : a = b) (h' : b = a) : P b := by simp [*] -- simp failed, maximum number of steps exceeded

Though, I'm not sure that protecting against this sort of thing is worth the overhead. I suppose it depends on how expensive making a simp set is.

Thomas Murrills (May 04 2023 at 23:59):

I do love the feature, though, especially when used in tandem with other lemmas e.g. whose arguments might need to be provided by hypotheses. That sort of interleaving effect can be very useful.

Kyle Miller (May 05 2023 at 08:39):

@Thomas Murrills Maybe a feature worth looking into is simp's discharger configuration. Doing simp (discharger := assumption) only is similar to simp only [*] if you're only wanting to use local hypotheses to close goals, but with the difference that assumption is more willing to do things up to defeq.


Last updated: Dec 20 2023 at 11:08 UTC