Zulip Chat Archive

Stream: general

Topic: Lean shows me the wrong goal?


Moritz R (Feb 26 2026 at 18:35):

Why does Lean show me the goal bla = bla` and not what i think to have specified?

opaque Calculus : Type u

-- opaque P : Calculus -> Prop
def P (c : Calculus) : Prop := sorry


class BaseSpecification where
  param : Nat





class ExtendedSpecification extends BaseSpecification where
  extra : Nat


def ExtendedSpecification.toCalc (e : ExtendedSpecification) : Calculus := sorry

/-- A base spec is made a calculus by extending it with some arbitrary parameter
      and converting that. -/
def BaseSpecification.toExtendSpecification (e : BaseSpecification) : ExtendedSpecification where
  extra := 0



def BaseSpecification.toCalc (e : BaseSpecification) : Calculus := e.toExtendSpecification.toCalc


-- i originally wanted to use this as a simp lemma, but i think it doesn't prove what i think it does..
-- goal and theorem hover shows `⊢ P e.toCalc ↔ P e.toCalc`
theorem the_problem (e : ExtendedSpecification) :
    P (ExtendedSpecification.toCalc e)
     P (BaseSpecification.toCalc e.toBaseSpecification) := by
  rfl

If one makes P opaque, then the proof by rfl doesnt go through, but i still get the wrong goal and the wrong theorem.
I wanted to use this theorem for rewriting, but it gets simplified to bla = bla somehow

Henrik Böving (Feb 26 2026 at 18:39):

toCalc elaborates to your rhs under the hood, there is no difference between the two (when using projections of the parent of a structure that was defined with structure Foo extends Bar implicitly resolving to a chain of projections is the intended behavior). The infoview recovers the goals you can see by looking at elaborated low level expressions (after all not all expressions in a goal are created from syntax but usually directly through meta programs) and trying to guess a syntax that could've produced this low level expression. Because both terms elaborate to the exact same expression it guesses the same resulting expression.

Henrik Böving (Feb 26 2026 at 18:41):

I guess there is an argument to be made to change the parent projection elaborator to not do this but the goal its showing is at least faithful to what is actually happening^^

Moritz R (Feb 26 2026 at 18:44):

It is faithful in the sense of showing defeq things, but that really is not faithful

Henrik Böving (Feb 26 2026 at 18:45):

No this is not about definitional equality, they are syntactically equal at the Expr level

Moritz R (Feb 26 2026 at 18:46):

oh what

Henrik Böving (Feb 26 2026 at 18:47):

That's what I was trying to get at, the declaration ExtendedSpecification.toCalc syntactically elaborates to BaseSpecification.toCalc e.toBaseSpecification, there is no difference on the Expr level.

Moritz R (Feb 26 2026 at 19:04):

Sorry i don't get how lean does this.
If i instead inline the definition i get the goal that i expect

theorem the_problem (e : ExtendedSpecification) :
    P (ExtendedSpecification.toCalc e)
     P (e.toBaseSpecification.toExtendSpecification.toCalc) := by
  rfl

Why is the goal here different?

Aaron Liu (Feb 26 2026 at 19:10):

this sounds very suspicious

Moritz R (Feb 26 2026 at 19:11):

Maybe the difference has to do with parsing of dot notation? Maybe som fun is inserted which makes the terms not elab identically?

Aaron Liu (Feb 26 2026 at 19:13):

Henrik Böving said:

That's what I was trying to get at, the declaration ExtendedSpecification.toCalc syntactically elaborates to BaseSpecification.toCalc e.toBaseSpecification, there is no difference on the Expr level.

This is false, they are different terms but are pretty printing the same

Henrik Böving (Feb 26 2026 at 19:13):

Interesting, David recently told me about this behavior and added special support for it in doc-gen because he observed that behavior.

Aaron Liu (Feb 26 2026 at 19:14):

if you have a : A and a function B.foo and A extends B then B.foo a.toB will pretty print as a.foo

Aaron Liu (Feb 26 2026 at 19:15):

in this case A.foo also exists and A.foo a also pretty prints as a.foo

Henrik Böving (Feb 26 2026 at 19:17):

Ah, david told me about a feature very specifically related to projections where this happens

Henrik Böving (Feb 26 2026 at 19:18):

I had mistakenly assumed without reading enough that toCalc is a projection where this would indeed be true. But here the expression is actually different then. Regardless the root cause for why you see a funny goal remains the same, the delaborator guesses the same syntax.

Henrik Böving (Feb 26 2026 at 19:21):

structure A where
  x : Nat

structure B extends A where
  y : String


set_option pp.raw true
example (b : B) : b.x = b.toA.x := rfl

this is the specific situation I had in mind (and accidentally pattern matched on to*)

Moritz R (Feb 26 2026 at 19:24):

Henrik Böving schrieb:

structure A where
  x : Nat

structure B extends A where
  y : String


set_option pp.raw true
example (b : B) : b.x = b.toA.x := rfl

this is the specific situation I had in mind (and accidentally pattern matched on to*)

I think for this situation it makes a lot of sense

Moritz R (Feb 26 2026 at 19:24):

Aaron Liu schrieb:

if you have a : A and a function B.foo and A extends B then B.foo a.toB will pretty print as a.foo

But i think this is a bit dangerous

Henrik Böving (Feb 26 2026 at 19:35):

I agree, there is probably some heuristic in the delaborator for dot notation on structures that's messing up to differentiate these situations a bit better. Maybe @Kyle Miller has an idea for what to do here?

Kyle Miller (Feb 26 2026 at 19:58):

Henrik Böving said:

Interesting, David recently told me about this behavior and added special support for it in doc-gen because he observed that behavior.

Could you point me to this change?

Henrik Böving (Feb 26 2026 at 19:59):

That was a wrong asessment by me only related to this type of behavior:

structure A where
  x : Nat

structure B extends A where
  y : String


set_option pp.raw true
example (b : B) : b.x = b.toA.x := rfl

the to* functions above are not the ones auto derived for extends but manually written out so my comment doesn't apply.

Kyle Miller (Feb 26 2026 at 20:02):

What's the pretty printer getting wrong? Both of these are true:

example (e : ExtendedSpecification) : e.toCalc = ExtendedSpecification.toCalc e := rfl
example (e : ExtendedSpecification) : e.toCalc = BaseSpecification.toCalc e.toBaseSpecification := rfl

Kyle Miller (Feb 26 2026 at 20:02):

By the way, you can use set_option pp.fieldNotation false rather than pp.raw to debug dot notation issues. It's a lot easier to read!

Kyle Miller (Feb 26 2026 at 20:06):

Certainly for simp purposes it's inconvenient that the pretty printed form isn't reducibly defeq:

example (e : ExtendedSpecification) : e.toCalc = ExtendedSpecification.toCalc e := by
  with_reducible rfl -- succeeds
example (e : ExtendedSpecification) : e.toCalc = BaseSpecification.toCalc e.toBaseSpecification := by
  with_reducible rfl -- fails

Henrik Böving (Feb 26 2026 at 20:09):

theorem the_problem (e : ExtendedSpecification) :
    P (ExtendedSpecification.toCalc e)
     P (BaseSpecification.toCalc e.toBaseSpecification) := by
  sorry

in this statement the ExtendedSpecification.toCalc is not necessarily the same as the BaseSpecification.toCalc (they are two different functions that could have different bodies) but if you inspect the pretty printed goal you see P e.toCalc ↔ P e.toCalc. Consider this version where the theorem is not provable:

opaque Calculus : Type u

opaque P (c : Calculus) : Prop

axiom c1 : Calculus
axiom c2 : Calculus

class BaseSpecification where
  param : Nat

class ExtendedSpecification extends BaseSpecification where
  extra : Nat


noncomputable def ExtendedSpecification.toCalc (e : ExtendedSpecification) : Calculus := c1

def BaseSpecification.toExtendSpecification (e : BaseSpecification) : ExtendedSpecification where
  extra := 0

noncomputable def BaseSpecification.toCalc (e : BaseSpecification) : Calculus := c2

theorem the_problem (e : ExtendedSpecification) :
    P (ExtendedSpecification.toCalc e)
     P (BaseSpecification.toCalc e.toBaseSpecification) := by
  sorry

the goal still reads

e : ExtendedSpecification
 P e.toCalc  P e.toCalc

Last updated: Feb 28 2026 at 14:05 UTC