Zulip Chat Archive

Stream: lean4

Topic: Unfold instance definition in a proof


Pavel Klimov (Sep 23 2025 at 02:31):

Is it possible to unfold definition of instance in a proof?

inductive Node
| leaf
| node (children : List Node)

mutual

def Node.beq : Node  Node  Bool
| .leaf, .leaf => true
| .node children1, .node children2 => Node.listBeq children1 children2
| _, _ => false

def Node.listBeq : List Node  List Node  Bool
| .nil, .nil => true
| x1 :: xs1, x2 :: xs2 => Node.beq x1 x2 && Node.listBeq xs1 xs2
| _, _ => false

end

instance nodeBEqInst : BEq Node where
  beq := Node.beq

in proofs, rw [BEq.beq] will translate == into nodeBEqInst.1 and then, is there way to convert it into Node.beq and use its definition?

Kenny Lau (Sep 23 2025 at 09:22):

@Pavel Klimov keep unfolding:

example (a b : Node) : a == b := by
  rw [BEq.beq, nodeBEqInst]
  dsimp only

Jovan Gerbscheid (Sep 23 2025 at 09:31):

In this case, simp only [BEq.beq] does the job, because simp only also expands projections.

Jovan Gerbscheid (Sep 23 2025 at 09:33):

You can also spell it like this: simp only [(· == ·)]

Pavel Klimov (Sep 23 2025 at 11:25):

@Kenny Lau Thanks! I tried only rw [nodeBEqInst.1] which didn't work. I didn't know about dsimp existence.
@Jovan Gerbscheid nice one!

Sad, though, it doesn't work if BEq instance is given by deriving statement :frown:

Kenny Lau (Sep 23 2025 at 11:25):

@Pavel Klimov that's because nodeBEqInst.1 is actually two things: the nodeBEqInst, and the .1.

Jovan Gerbscheid (Sep 23 2025 at 11:33):

Yes, if you want to prove anything about the instance, you shouldn't use deriving. I would have suggested trying deriving DecidableEq, but that doesn't seem to work here.

Markus Himmel (Sep 23 2025 at 11:37):

Jovan Gerbscheid said:

Yes, if you want to prove anything about the instance, you shouldn't use deriving

This is (slowly) changing! In the latest nightly, there is now a reduceBEq simproc which will unfold derived BEq instances. Sadly, for now this only works for non-nested inductives, so it's not useful for Pavel Klimov's example yet.

Pavel Klimov (Sep 23 2025 at 20:06):

@Kenny Lau what is .1 then? First element of what? Can I specify in rw or simp exactly .1 part of nodeBEqInst?

Kenny Lau (Sep 23 2025 at 20:08):

@Pavel Klimov it's some built-in notation that means "the first part of the structure"

Kenny Lau (Sep 23 2025 at 20:10):

I'm getting a bit meta here, but the corresponding Lean.Expr is docs#Lean.Expr.proj (ignore the meta stuff if it confuses you); the point here is that it's a separate atomic notation and not based on anything else

Robin Arnez (Sep 23 2025 at 20:10):

Well, nodeBEqInst is of type BEq Node and BEq is a structure with the first field BEq.beq; usually you'd have nodeBEqInst.beq a b (which gets the a == b) notation but you unfolded that so you get what's called a primitive projection which is a detail of how projections are supported in Lean.

Kenny Lau (Sep 23 2025 at 20:12):

for example, a ↔ b is another structure with the first field being a → b and the second field being b → a, so if you have h : a ↔ b, then you have access to h.1 : a → b and h.2 : b → a.
(https://leanprover-community.github.io/mathlib4_docs/Init/Core.html#Iff)

Robin Arnez (Sep 23 2025 at 20:16):

If you write .1 or .2 etc. yourself, you're usually not going to get a primitive projection but a projection function; these projection functions are implemented using primitive projections though. Primitive projections exist for inductives with exactly one constructor (e.g. structures) and are basically equivalent to a match that extracts the nth element:

inductive ABC where
  | xyz (x : Nat) (y : Bool)

example (x : ABC) :
    x.1 =
      match x with
      | .xyz a _ => a := rfl

example (x : ABC) :
    x.2 =
      match x with
      | .xyz _ b => b := rfl

Kenny Lau (Oct 23 2025 at 15:13):

so somehow nobody here provided the correct answer, which I only discovered recently thanks to @Aaron Liu :

@Pavel Klimov the way to do it is by unfold_projs

import Mathlib.Data.Set.Defs
import Mathlib.Tactic.DefEqTransformations

example (X : Set Nat) : X  X := by
  unfold_projs
  guard_target =ₛ Set.Subset X X
  exact fun x hx  hx

(which I now realise is in mathlib not core)

Aaron Liu (Oct 23 2025 at 18:26):

I didn't realize it was the "correct" answer

Ruben Van de Velde (Oct 23 2025 at 18:30):

It's not a great idea to use that in code you want to maintain, I wouldn't say

Ruben Van de Velde (Oct 23 2025 at 18:31):

The correct answer is rw [Set.subset_def]


Last updated: Dec 20 2025 at 21:32 UTC