Zulip Chat Archive

Stream: metaprogramming / tactics

Topic: Goal changing when hypothesis is introduced


Anirudh Suresh (Jun 05 2025 at 10:56):

import Mathlib
import Lean
import Lean.Elab.Tactic
import Lean.Elab.Term
import Lean.Meta

open Lean
open Lean.Elab.Tactic
open Lean.Elab.Term
open Lean.Meta
open scoped Matrix
open Matrix


def com_equiv (c1 c2 : ) : Prop :=
   n : , c1 + n = c2 + n

infix:80 " ≡ " => com_equiv

namespace Expr

partial def matchApp? (e : Expr) : Option (Expr × Array Expr) :=
  match e with
  | Expr.app f x =>
    match matchApp? f with
    | some (head, args) => some (head, args.push x)
    | none              => some (f, #[x])
  | _ => none

end Expr

partial def matchAppDebug (e : Expr) : MetaM (Option (Expr × Array Expr)) := do

  logInfo m!"[matchAppDebug] incoming e: {e}"
  match e with
  | Expr.app f x =>
    match  matchAppDebug f with
    | some (head, args) =>
      pure (some (head, args.push x))
    | none =>
      pure (some (f, #[x]))
  | _ =>
    logInfo "[matchAppDebug] not an `app`, returning none"
    pure none

elab "dbg_match_goalc" : tactic => do
  let goalExpr  getMainTarget
  logInfo m!"[dbg_match_goalc] goalExpr = {goalExpr}"
  let some _  matchAppDebug goalExpr |
    logInfo "[dbg_match_goalc] matchAppDebug returned none,   not an application";
    pure ()
  pure ()
lemma l1 {a b: }: a b:= by {
  dbg_match_goalc
}
lemma l2 {a b: }: a b:= by {
  have h:1=1:=by simp
  dbg_match_goalc
}

In the code above, I am trying to create a tactic. However, the tactic behaves differently with the introduction of a hypothesis. The debug statement in both l1 and l2 show goalExpr to be the same thing but for some reason, only l1 detects the com_equiv. Is there a way to fix this so that the tactic can work irrespective of the presence of a hypothesis?

Aaron Liu (Jun 05 2025 at 11:01):

You have to consume the metadata:

partial def matchAppDebug (e : Expr) : MetaM (Option (Expr × Array Expr)) := do

  logInfo m!"[matchAppDebug] incoming e: {e}"
  match e.consumeMData with
  | Expr.app f x =>
    match  matchAppDebug f with
    | some (head, args) =>
      pure (some (head, args.push x))
    | none =>
      pure (some (f, #[x]))
  | _ =>
    logInfo "[matchAppDebug] not an `app`, returning none"
    pure none

Damiano Testa (Jun 05 2025 at 11:07):

Also, generally matching directly on expressions is very brittle. You may consider using getMainTarget'', or explicitly adding a whnfR, which is what most mathlib tactic use.

Damiano Testa (Jun 05 2025 at 11:09):

Moreover, if you want your tactic to be aware of the new hypotheses, you should probably add a withMainContext at the very beginning of your tactic definition.


Last updated: Dec 20 2025 at 21:32 UTC