Zulip Chat Archive

Stream: metaprogramming / tactics

Topic: isDefEq proofIrrelevance


Chase Norman (Jul 28 2025 at 01:57):

I'm having trouble with the following isDefEq query between two terms that are identical, except for a subterm replaced with a metavariable:

import Lean
open Lean Meta Expr Elab Tactic Core

axiom p : Unit   x : Unit, True
noncomputable def concrete := fun x => (p x).choose

-- set_option trace.Meta.isDefEq true
#eval (do
  let value := ( getConstInfo ``concrete).value!
  let e1 := value.bindingBody!.instantiate1 (.const ``Unit.unit [])
  let e2 := value.bindingBody!.instantiate1 ( mkFreshExprMVar (Expr.const ``Unit []))
  withConfig (fun c => { c with proofIrrelevance := false }) do
    let _  isDefEq e1 e2
    dbg_trace ( instantiateMVars e2)
)

Looking at the trace, the metavariable is never assigned, probably due to proof irrelevance. However, I've specifically set proofIrrelevance to false in the Config. Is there a way that I can get a more syntactic match in this case?

Justin Asher (Jul 28 2025 at 02:57):

Isn't this due to choose being opaque?

Robin Arnez (Jul 28 2025 at 07:06):

Maybe it's applying subsingleton elimination?

Kyle Miller (Jul 28 2025 at 08:20):

It looks like isDefEqArgsFirstPass in ExprDefEq implicitly encodes proof irrelevance. When proof arguments are being compared, then the arguments are completely skipped if neither is of the form fun x_1 ... x_n => ?m y_1 ... y_m, where n and m can be 0. Here, p () =?= p ?_ is being skipped.

It's probably worth creating an issue.

Chase Norman (Jul 29 2025 at 03:43):

Thanks @Kyle Miller for the really helpful analysis. I have created an issue here: https://github.com/leanprover/lean4/issues/9612.

:+1:'s on the issue are appreciated, as well as ideas for how to work around the issue. I'd like to release a feature for Canonical that depends on a guarantee that a successful isDefEq query between a term with metavariables and a term without metavariables always assigns all of the metavariables, even if they are in proof arguments.

Chase Norman (Jul 29 2025 at 16:10):

Here's my workaround:

def isDefEqForce (u v : Expr) : MetaM Bool := do
  match u, v with
  | app f a, app f' a' => isDefEqForce f f' <&&> isDefEqForce a a'
  | _, _ => isDefEqGuarded u v

Last updated: Dec 20 2025 at 21:32 UTC