Zulip Chat Archive

Stream: new members

Topic: unexpected close of goal with rfl


Andre Maute (Feb 11 2026 at 11:26):

Hi there,

I'm experimenting with transporting type classes,

My stripped down example below consists of an equivalence of two data clases 'Square' and 'SquareFin'.

There are structures 'Parent', 'ParenFin', 'Child' and 'ChildFin',
where the 'Child' structures extend the 'Parent' ones.

For the 'Child' class there exists a map 'Child.flip' mapping child to child.
I do NOT want to define 'ChildFin.flip'.

Accordingly to the parallel construction two type classes are defined
'FlippySquare' and 'FlippySquareFin'
both have a property 'is_flippy'.

Question:
Proving 'is_flippy' for an instance of 'FlippySquareFin'
can be done without ever resorting to an 'is_flippy' from 'Child' 'ChildFin' or even 'FlippySquare'
rfl closes the goal, what am I missing here?

I tested my mwe on live.lean-lang.org with the latest Mathlib selected

Best regards
Andre

import Mathlib.Tactic

@[ext]
structure Square (d : Nat) where
  T : Nat  Nat  Nat
  Tinv : Nat  Nat  Nat
  is_T_null_row :
     k l : Nat,
      k > d
         T k l = d+1
  is_T_null_col :
     k l : Nat,
      l > d
         T k l = d+1
  is_Tinv_null_row :
     k l : Nat,
      k > d
         Tinv k l = d+1
  is_Tinv_null_col :
     k l : Nat,
      l > d
         Tinv k l = d+1
  is_T_le :
     k l : Nat,
      k  d  l  d
         T k l  d
  is_Tinv_le :
     k l : Nat,
      k  d  l  d
         Tinv k l  d
  is_Tinv_T_id :
     k l : Nat,
      k  d  l  d
         (Tinv k (T k l) = l)
  is_T_Tinv_id :
     k l : Nat,
      k  d  l  d
         (T k (Tinv k l) = l)

@[ext]
structure SquareFin (d : Nat) where
  T : Fin (d + 1)  Equiv.Perm (Fin (d + 1))

def squareEquivPerm (s : Square d) (k : Fin (d + 1)) : Equiv.Perm (Fin (d + 1)) where
  toFun l := by sorry
  invFun l := by sorry
  left_inv := by sorry
  right_inv := by sorry

def SquareEquivSquareFin_toFun (s : Square d)
: SquareFin d where
  T := fun k => squareEquivPerm s k

def SquareEquivSquareFin_invFun (s : SquareFin d)
: Square d where
  T := by sorry
  Tinv := by sorry
  is_T_null_row := by sorry
  is_T_null_col := by sorry
  is_Tinv_null_row := by sorry
  is_Tinv_null_col := by sorry
  is_T_le := by sorry
  is_Tinv_le := by sorry
  is_Tinv_T_id := by sorry
  is_T_Tinv_id := by sorry

theorem SquareEquivSquareFin_right_inv
:  s : SquareFin d,
  SquareEquivSquareFin_toFun (SquareEquivSquareFin_invFun s) = s
:= by sorry

-------

@[ext]
class FlippySquare (d : Nat) (α : Type) where
  T : α  Square d
  flip : α  α
  is_flippy :
     a : α,  k l : Nat,
      k  d  l  d
       (T (flip a)).T k l = (T a).T l k

@[ext]
class FlippySquareFin (d : Nat) (α : Type) where
  T : α  SquareFin d
  flip : α  α
  is_flippy :
     a : α,  k l : Fin (d + 1),
      (T (flip a)).T k l = (T a).T l k

-------

@[ext]
structure Parent (d : Nat) where
  T : Square d

@[ext]
structure ParentFin (d : Nat) where
  T : SquareFin d

-------

def ParentEquivParentFin_toFun (s : Parent d)
: ParentFin d where
  T := SquareEquivSquareFin_toFun s.T

def ParentEquivParentFin_invFun (s : ParentFin d)
: Parent d where
  T := SquareEquivSquareFin_invFun s.T

def ParentEquivParentFin : Parent d  ParentFin d where
  toFun s := ParentEquivParentFin_toFun s
  invFun s := ParentEquivParentFin_invFun s
  left_inv := by sorry
  right_inv := by sorry

-------

@[ext]
structure Child (d : Nat)
extends Parent d where

@[ext]
structure ChildFin (d : Nat)
extends ParentFin d where

def ChildEquivChildFin_toFun (s : Child d)
: ChildFin d where
  T := (ParentEquivParentFin_toFun s.toParent).T

def ChildEquivChildFin_invFun (s : ChildFin d)
 : Child d where
  T := (ParentEquivParentFin_invFun s.toParentFin).T

theorem ChildEquivChildFin_left_inv
:  s : Child d,
  ChildEquivChildFin_invFun (ChildEquivChildFin_toFun s) = s
:= by sorry

theorem ChildEquivChildFin_right_inv
:  s : ChildFin d,
  ChildEquivChildFin_toFun (ChildEquivChildFin_invFun s) = s
:= by sorry

def ChildEquivChildFin
: Child d  ChildFin d where
  toFun s := ChildEquivChildFin_toFun s
  invFun s := ChildEquivChildFin_invFun s
  left_inv := by sorry
  right_inv := by sorry

def Child.flip (a : Child d)
: Child d where
  T := by sorry

--- Child.is_flippy is missing intentionally

instance instFlippyChildFin
: FlippySquareFin d (ChildFin d) where
  T := fun a => SquareEquivSquareFin_toFun ((ChildEquivChildFin_invFun a).T)
  flip := fun a => ChildEquivChildFin_toFun ((ChildEquivChildFin_invFun a).flip)
  is_flippy := by
    refine fun a k l => ?_
    unfold ChildEquivChildFin_toFun
    unfold ChildEquivChildFin_invFun
    unfold ParentEquivParentFin_toFun
    unfold ParentEquivParentFin_invFun
    simp
    ext
    rw [SquareEquivSquareFin_right_inv]
    -- Question: Why does rfl close the goal?
    rfl

Andre Maute (Feb 11 2026 at 11:27):

FYi.
the rfl is on the last line of the mwe

Robin Arnez (Feb 11 2026 at 11:36):

Well, both sides reduce to the same sorry in toFun in squareEquivPerm

Robin Arnez (Feb 11 2026 at 12:09):

Specifically, consider

instance instFlippyChildFin
: FlippySquareFin d (ChildFin d) where
  T := fun a => SquareEquivSquareFin_toFun ((ChildEquivChildFin_invFun a).T)
  flip := fun a => ChildEquivChildFin_toFun ((ChildEquivChildFin_invFun a).flip)
  is_flippy a k l := by
    unfold SquareEquivSquareFin_toFun
    dsimp only -- reduce the { T := ... }.T
    unfold squareEquivPerm
    rewrite [Equiv.coe_fn_mk]

Andre Maute (Feb 11 2026 at 18:48):

if add some conv whnf combinations before the rfl (a hint from Robin), and prepare that state into a theorem

instance instFlippyChildFin
: FlippySquareFin d (ChildFin d) where
  T := fun a => SquareEquivSquareFin_toFun ((ChildEquivChildFin_invFun a).T)
  flip := fun a => ChildEquivChildFin_toFun ((ChildEquivChildFin_invFun a).flip)
  is_flippy := by
    refine fun a k l => ?_
    unfold ChildEquivChildFin_toFun
    unfold ChildEquivChildFin_invFun
    unfold ParentEquivParentFin_toFun
    unfold ParentEquivParentFin_invFun
    simp
    ext
    rw [SquareEquivSquareFin_right_inv]
    conv => lhs; whnf
    conv => rhs; whnf
    -- Question: Why does rfl close the goal?
    rfl
theorem xxx (a : ChildFin d)
:
   k l : Fin (d + 1),
 (((SquareEquivSquareFin_toFun  (SquareEquivSquareFin_invFun a.T).flip.T).T k) l).1 =
  (((SquareEquivSquareFin_toFun (SquareEquivSquareFin_invFun a.T)).T l) k).1
:= by sorry

one sees in the web ide that the flip has a problem, because there is no Square.flip def
so my question still applies why does rfl close the goal.

Robin Arnez (Feb 11 2026 at 19:01):

The main reason is that the first sorry in squareEquivPerm does not depend on s, k or l, so it is the same regardless of which value you provide

Andre Maute (Feb 11 2026 at 19:11):

I will provide the full implementation for the squareEquivPerm. give me some minutes

Andre Maute (Feb 11 2026 at 19:20):

I can't provide my full not-stripped-down proof.
But the rfl doesn't close now. But In my not-stripped-down proof it still closes. hmm...

import Mathlib.Tactic

@[ext]
structure Square (d : Nat) where
  T : Nat  Nat  Nat
  Tinv : Nat  Nat  Nat
  is_T_null_row :
     k l : Nat,
      k > d
         T k l = d+1
  is_T_null_col :
     k l : Nat,
      l > d
         T k l = d+1
  is_Tinv_null_row :
     k l : Nat,
      k > d
         Tinv k l = d+1
  is_Tinv_null_col :
     k l : Nat,
      l > d
         Tinv k l = d+1
  is_T_le :
     k l : Nat,
      k  d  l  d
         T k l  d
  is_Tinv_le :
     k l : Nat,
      k  d  l  d
         Tinv k l  d
  is_Tinv_T_id :
     k l : Nat,
      k  d  l  d
         (Tinv k (T k l) = l)
  is_T_Tinv_id :
     k l : Nat,
      k  d  l  d
         (T k (Tinv k l) = l)

@[ext]
structure SquareFin (d : Nat) where
  T : Fin (d + 1)  Equiv.Perm (Fin (d + 1))

def squareTFin (s : Square d) (k l : Fin (d + 1)) : Fin (d + 1) where
  val := s.T k.val l.val
  isLt := by
    refine Nat.lt_add_one_iff.mpr ?_
    exact s.is_T_le k l k.is_le l.is_le

def squareTinvFin (s : Square d) (k l : Fin (d + 1)) : Fin (d + 1) where
  val := s.Tinv k.val l.val
  isLt := by
    refine Nat.lt_add_one_iff.mpr ?_
    exact s.is_Tinv_le k l k.is_le l.is_le

def squareEquivPerm (s : Square d) (k : Fin (d + 1)) : Equiv.Perm (Fin (d + 1)) where
  toFun l := squareTFin s k l
  invFun l := squareTinvFin s k l
  left_inv := by
    unfold Function.LeftInverse
    intro l
    refine Fin.ext_iff.mpr ?_
    unfold squareTFin
    unfold squareTinvFin
    simp
    refine s.is_Tinv_T_id k.val l.val k.is_le l.is_le
  right_inv := by
    unfold Function.RightInverse
    intro l
    refine Fin.ext_iff.mpr ?_
    unfold squareTinvFin
    unfold squareTFin
    simp
    refine s.is_T_Tinv_id k.val l.val k.is_le l.is_le

def SquareEquivSquareFin_toFun (s : Square d)
: SquareFin d where
  T := fun k => squareEquivPerm s k

def SquareEquivSquareFin_invFun (s : SquareFin d)
: Square d where
  T := by sorry
  Tinv := by sorry
  is_T_null_row := by sorry
  is_T_null_col := by sorry
  is_Tinv_null_row := by sorry
  is_Tinv_null_col := by sorry
  is_T_le := by sorry
  is_Tinv_le := by sorry
  is_Tinv_T_id := by sorry
  is_T_Tinv_id := by sorry

theorem SquareEquivSquareFin_right_inv
:  s : SquareFin d,
  SquareEquivSquareFin_toFun (SquareEquivSquareFin_invFun s) = s
:= by sorry

-------

@[ext]
class FlippySquare (d : Nat) (α : Type) where
  T : α  Square d
  flip : α  α
  is_flippy :
     a : α,  k l : Nat,
      k  d  l  d
       (T (flip a)).T k l = (T a).T l k

@[ext]
class FlippySquareFin (d : Nat) (α : Type) where
  T : α  SquareFin d
  flip : α  α
  is_flippy :
     a : α,  k l : Fin (d + 1),
      (T (flip a)).T k l = (T a).T l k

-------

@[ext]
structure Parent (d : Nat) where
  T : Square d

@[ext]
structure ParentFin (d : Nat) where
  T : SquareFin d

-------

def ParentEquivParentFin_toFun (s : Parent d)
: ParentFin d where
  T := SquareEquivSquareFin_toFun s.T

def ParentEquivParentFin_invFun (s : ParentFin d)
: Parent d where
  T := SquareEquivSquareFin_invFun s.T

def ParentEquivParentFin : Parent d  ParentFin d where
  toFun s := ParentEquivParentFin_toFun s
  invFun s := ParentEquivParentFin_invFun s
  left_inv := by sorry
  right_inv := by sorry

-------

@[ext]
structure Child (d : Nat)
extends Parent d where

@[ext]
structure ChildFin (d : Nat)
extends ParentFin d where

def ChildEquivChildFin_toFun (s : Child d)
: ChildFin d where
  T := (ParentEquivParentFin_toFun s.toParent).T

def ChildEquivChildFin_invFun (s : ChildFin d)
 : Child d where
  T := (ParentEquivParentFin_invFun s.toParentFin).T

theorem ChildEquivChildFin_left_inv
:  s : Child d,
  ChildEquivChildFin_invFun (ChildEquivChildFin_toFun s) = s
:= by sorry

theorem ChildEquivChildFin_right_inv
:  s : ChildFin d,
  ChildEquivChildFin_toFun (ChildEquivChildFin_invFun s) = s
:= by sorry

def ChildEquivChildFin
: Child d  ChildFin d where
  toFun s := ChildEquivChildFin_toFun s
  invFun s := ChildEquivChildFin_invFun s
  left_inv := by sorry
  right_inv := by sorry

def Child.flip (a : Child d)
: Child d where
  T := by sorry

--- Child.is_flippy is missing intentionally

instance instFlippyChildFin
: FlippySquareFin d (ChildFin d) where
  T := fun a => SquareEquivSquareFin_toFun ((ChildEquivChildFin_invFun a).T)
  flip := fun a => ChildEquivChildFin_toFun ((ChildEquivChildFin_invFun a).flip)
  is_flippy := by
    refine fun a k l => ?_
    unfold ChildEquivChildFin_toFun
    unfold ChildEquivChildFin_invFun
    unfold ParentEquivParentFin_toFun
    unfold ParentEquivParentFin_invFun
    simp
    ext
    rw [SquareEquivSquareFin_right_inv]
    -- Question: Why does rfl close the goal?
    -- with the squareEquivPerm implementation rfl doesn't close
    rfl

Andre Maute (Feb 11 2026 at 21:24):

My not-stripped-down proof looks now good to me.
Essentially it boils down to have implementations for the Child.flip def
using some intermediate def Child.flip_Square

def Child.flip_Square (a :ChildSquare d) : Square (d+1)

which then allows to remove the flip.
I don't need to use a Child.is_flippy theorem here either as thought in the beginning.

The confusion must have come from too much sorries, as Robin pointed out.

Best regards and thank you very much Robin, that helped me a lot
Andre


Last updated: Feb 28 2026 at 14:05 UTC