Zulip Chat Archive
Stream: metaprogramming / tactics
Topic: Basic Qq help - matching against both goal and hypothesis
Dessertion (Mar 03 2025 at 19:26):
What would be the Lean equivalent of the following Coq tactic?
Ltac match_on_conj :=
match goal with
| [ H : ?P /\ ?Q |- ?P ] => apply (proj1 H)
| [ H : ?P /\ ?Q |- ?Q ] => apply (proj2 H)
end.
Example E1 {P Q : Prop} (H : P /\ Q) : P.
match_on_conj.
Qed.
Example E2 {P Q : Prop} (H : P /\ Q) : Q.
match_on_conj.
Qed.
This is my very crude attempt at a translation:
import Qq
open Qq Lean Elab Meta Tactic
elab "match_on_conj" : tactic => do
for ldecl in ← getLCtx do
let ⟨0, type, _⟩ := ← inferTypeQ ldecl.toExpr | continue
match type with
| ~q($p ∧ $q) =>
have goalType : Q(Prop) := ← getMainTarget
let hypName := mkIdent ldecl.userName
if let ~q($x) := goalType then
if ← isDefEq x p then
evalTactic (← `(tactic| apply And.left $hypName))
else if ← isDefEq x q then
evalTactic (← `(tactic| apply And.right $hypName))
else pure ()
| _ => pure ()
example {P Q : Prop} (H : P ∧ Q) : P := by
match_on_conj
example {P Q : Prop} (H : P ∧ Q) : Q := by
match_on_conj
Is there a way to directly match the goal against my hypotheses as in Coq, without resorting to isDefEq
? And more generally, what would be a more idiomatic way to write something like this?
Aaron Liu (Mar 03 2025 at 20:04):
Maybe something like this? Not sure how this generalizes to more complicated stuff though.
macro "match_on_conj" : tactic =>
`(tactic| first | exact And.left ‹_› | exact And.right ‹_› | fail "tactic 'match_on_conj' failed")
example {P Q : Prop} (H : P ∧ Q) : P := by
match_on_conj
example {P Q : Prop} (H : P ∧ Q) : Q := by
match_on_conj
Dessertion (Mar 03 2025 at 20:20):
I suppose that does work for this toy example, but I would still like to know how one would implement this from "first principles" (vs. directly chaining pre-existing tactics).
Aaron Liu (Mar 03 2025 at 20:46):
Metaprogramming in Lean did not cover Qq, so I don't know how to use it. Here's my solution:
import Lean
open Lean Elab Meta Tactic
elab "match_on_conj" : tactic => do
let goal := ← getMainGoal
let goalType ← goal.getType'
for ldecl in ← getLCtx do
if ldecl.isImplementationDetail then continue
let type ← instantiateMVars (← whnf ldecl.type)
let some (p, q) := type.app2? ``And | continue
if ← withReducible (isDefEq p goalType) then
goal.assign (mkApp3 (.const ``And.left []) p q ldecl.toExpr)
else if ← withReducible (isDefEq q goalType) then
goal.assign (mkApp3 (.const ``And.right []) p q ldecl.toExpr)
else
throwTacticEx `match_on_conj goal
example {P Q : Prop} (H : P ∧ Q) : P := by
match_on_conj
example {P Q : Prop} (H : P ∧ Q) : Q := by
match_on_conj
Vasilii Nesterov (Mar 05 2025 at 12:12):
This is an example with Qq:
import Qq
open Qq Lean Elab Meta Tactic
elab "match_on_conj" : tactic => do
for ldecl in ← getLCtx do
if ldecl.isImplementationDetail then continue
let ⟨u, type, expr⟩ ← inferTypeQ ldecl.toExpr
if !(← isProp type) then continue
have : u =QL 0 := ⟨⟩
match type with
| ~q($p ∧ $q) =>
let goal ← getMainGoal
let goalType ← getMainTarget
if ← isDefEq goalType p then
goal.assign q(And.left $expr)
else if ← isDefEq goalType q then
goal.assign q(And.right $expr)
| _ => pure ()
example {P Q : Prop} (H : P ∧ Q) : P := by
match_on_conj
example {P Q : Prop} (H : P ∧ Q) : Q := by
match_on_conj
example {P Q R : Prop} (h1 : P → Q ∧ R) (h2 : P) : Q := by
have h3 := h1 h2
match_on_conj
Note that if we write
let ⟨0, type, _⟩ := ← inferTypeQ ldecl.toExpr | continue
instead of
let ⟨u, type, expr⟩ := ← inferTypeQ ldecl.toExpr
if !(← isProp type) then continue
have : u =QL 0 := ⟨⟩
then we lose hypotheses whose u
is a metavariable and the last example fails.
Last updated: May 02 2025 at 03:31 UTC