Zulip Chat Archive

Stream: Is there code for X?

Topic: low level tactics


Paige Thomas (Sep 21 2024 at 18:44):

Is there a tactic that does nothing but expand notation? If not, how could that be done?

Ruben Van de Velde (Sep 21 2024 at 18:45):

set_option pp.notation false?

Edward van de Meent (Sep 21 2024 at 18:46):

if by notation you mean abbrev (and possibly def), using a tactic like dsimp might be what you're looking for

Paige Thomas (Sep 21 2024 at 18:47):

I was thinking along the lines of the ++ in

example
  {α : Type}
  (l : List α) :
  [] ++ l = l :=

Paige Thomas (Sep 21 2024 at 18:48):

And the lowest level set of tactics (tactics that do the least amount of work) that would solve this.

Paige Thomas (Sep 21 2024 at 18:49):

To make the proof as explicit as possible.

Ruben Van de Velde (Sep 21 2024 at 18:49):

Do you mean specifically "what does the HAppend instance for this type do"?

Kyle Miller (Sep 21 2024 at 18:50):

rfl should prove that one

Paige Thomas (Sep 21 2024 at 18:51):

I wanted to avoid rfl, because I think it does things I want to make explicit.

Paige Thomas (Sep 21 2024 at 18:52):

Ruben Van de Velde said:

Do you mean specifically "what does the HAppend instance for this type do"?

maybe? I didn't even know about HAppend until I tried the suggested set_option pp.notation false.

Edward van de Meent (Sep 21 2024 at 18:52):

you can hover over the term in the infoview to see what the notation means

Edward van de Meent (Sep 21 2024 at 18:53):

at that point, you typically can do some kind of dsimp only [HAppend.hAppend] (or whatever is the head term here) to simplify/reduce

Paige Thomas (Sep 21 2024 at 18:54):

Does dsimp unfold the definition like delta and nothing more?

Edward van de Meent (Sep 21 2024 at 18:55):

i did not know about delta, i think that indeed is a better tactic for the occasion.

Edward van de Meent (Sep 21 2024 at 18:57):

dsimp can do more, but it still guarantees that the result is defeq to the input. (afaict delta does this too)

Paige Thomas (Sep 21 2024 at 19:07):

If I did this

example
  {α : Type}
  (l : List α) :
  List.append [] l = l :=
  by
    delta List.append
α : Type
l : List α
 List.brecOn (motive := fun x => List α  List α) []
    (fun x f x_1 =>
      (match (motive := (x : List α)  List α  List.below (motive := fun x => List α  List α) x  List α) x, x_1 with
        | [], r => fun x => r
        | a :: l, r => fun x => a :: x.fst.fst r)
        f)
    l =
  l

Is there a tactic that would only apply the match and not close the goal?

Paige Thomas (Sep 21 2024 at 19:09):

So that I would end up with l = l, by definition of the match?

Kyle Miller (Sep 21 2024 at 19:11):

This fact is immediate from the definition of List.append though. You can apply the equation lemma that's auto-generated from the definition:

example
  {α : Type}
  (l : List α) :
  [] ++ l = l :=
  List.append.eq_1 l

Kyle Miller (Sep 21 2024 at 19:13):

If you are using mathlib, you could use unfold_projs to unfold ++ to the underlying function.

example
  {α : Type}
  (l : List α) :
  [] ++ l = l := by
  unfold_projs  -- unfold (reduce) the `HAppend.hAppend` instance.
  -- ⊢ [].append l = l
  unfold List.append -- use the equation lemma
  -- ⊢ l = l
  rfl

Kyle Miller (Sep 21 2024 at 19:15):

delta unfolds to the underlying term, and it doesn't use equation lemmas or "smart unfolding". Regarding your question, you could use reduce from mathlib to get l = l. You can also use dsimp only, but it will see that the result is l = l and automatically close the goal with rfl.

Kyle Miller (Sep 21 2024 at 19:16):

reduce will unfold definitions though, just like rfl, so if you're wanting to very carefully control reduction, you shouldn't use it:

example
  {α : Type}
  (l : List α) :
  [] ++ l = l := by
  reduce
  -- ⊢ l = l
  rfl

Kyle Miller (Sep 21 2024 at 19:18):

You can take a look at docs#Lean.Meta.DSimp.Config to see all the things you can configure dsimp to do. Configuration options go inside the {} in dsimp (config := {}) only.

Paige Thomas (Sep 21 2024 at 19:37):

Kyle Miller said:

If you are using mathlib, you could use unfold_projs to unfold ++ to the underlying function.

example
  {α : Type}
  (l : List α) :
  [] ++ l = l := by
  unfold_projs  -- unfold (reduce) the `HAppend.hAppend` instance.
  -- ⊢ [].append l = l
  unfold List.append -- use the equation lemma
  -- ⊢ l = l
  rfl

This is exactly what I was looking for.

Paige Thomas (Sep 21 2024 at 19:38):

Thank you!

Paige Thomas (Sep 21 2024 at 19:40):

What are "projections of class instances at the given location" in the doc for unfold_projs?

Paige Thomas (Sep 21 2024 at 20:19):

Is there a way to do this, but not have it close the goal, but instead end up with a :: as.append bs = a :: as.append bs? I'm guessing being inside the conv makes it close it?

lemma List.cons_append
  {α : Type}
  (a : α)
  (as bs : List α) :
  (a::as) ++ bs = a::(as ++ bs) := by
  unfold_projs
  conv => left; unfold List.append

Kyle Miller (Sep 21 2024 at 20:20):

Recall that [] ++ l is notation for @HAppend.hAppend (List α) (List α) (List α) inst [] l, where inst stands for the HAppend instance for List α. The hAppend function is a projection for the hAppend field of HAppend. That's to say, this is inst.hAppend [] l. The unfold_projs tactic expands inst and gets the hAppend field out. This reduces it to List.append [] l after two steps (there's also an Append instance).

Kyle Miller (Sep 21 2024 at 20:20):

Yeah, conv closes goals using with_reducible rfl

Paige Thomas (Sep 21 2024 at 20:26):

Is there a way to make it not do that, or an alternative similar tactic that won't?

Kyle Miller (Sep 21 2024 at 20:28):

No, I don't believe so. You'd have to make your own.

Can you instead use a sequence of change tactics? Trying to unfold things like this using tactics is fairly artificial for Lean, and you'll be fighting against many tactics to never do rfl or auto-solve other trivial goals.

Kyle Miller (Sep 21 2024 at 20:29):

I'm imagining something like

lemma List.cons_append'
    {α : Type}
    (a : α)
    (as bs : List α) :
    (a::as) ++ bs = a::(as ++ bs) := by
  change (a :: as).append bs = a :: as.append bs
  change a :: as.append bs = a :: as.append bs
  rfl

Paige Thomas (Sep 21 2024 at 20:32):

It would be great if there were more lower level/finer grained tactics.

Paige Thomas (Sep 21 2024 at 20:35):

Maybe starting with one for each application of the typing rules, and building up.

Paige Thomas (Sep 21 2024 at 20:36):

I think it might help make some things clearer, at least for me.

Paige Thomas (Sep 21 2024 at 20:54):

I don't know if maybe it would also help with translating between other proof systems?

Kevin Buzzard (Sep 22 2024 at 11:09):

I wouldn't need such tactics because I just want to get goals closed. But if they're useful for what you're doing then lean 4 makes them pretty easy to write

Paige Thomas (Sep 22 2024 at 16:42):

I started looking at how to do that.


Last updated: May 02 2025 at 03:31 UTC