Zulip Chat Archive
Stream: mathlib4
Topic: Matching compositions with Qq
Adam Topaz (Nov 16 2023 at 03:35):
Does anyone know of any examples of meta code which uses Qq to match compositions of morphisms in a category?
Ideally I want something of type Expr -> MetaM (Expr x Expr)
which will split out f
and g
(as exprs) given f ≫ g
as input.
Scott Morrison (Nov 16 2023 at 03:56):
I did this long long ago, but I'm sure I've lost the branch. :-(
Adam Topaz (Nov 16 2023 at 04:23):
In the meantime, I'm using
import Mathlib
open Lean Elab Meta Term Command Qq
open CategoryTheory
-- Given an expression of the composition `f ≫ g`, return `f` and `g`.
def Lean.Expr.matchCategoryStructComp (e : Expr) : MetaM (Expr × Expr) :=
match e with
| .app (.app (.app (.app (.app (.app (.app
(.const ``CategoryStruct.comp [_v, _u]) _obj) _inst) _X) _Y) _Z) f) g =>
return (f, g)
| _ => failure
elab "foo" t:term : command => Elab.Command.runTermElabM fun _ => do
let e ← withSynthesize <| Elab.Term.elabTerm t none
let e ← instantiateMVars e
let (f,g) ← e.matchCategoryStructComp
logInfo m!"{f}, {g}"
variable (C : Type*) [Category C] (X Y Z : C) (f : X ⟶ Y) (g : Y ⟶ Z)
foo f ≫ g
I would love to see how to Qq can be used for this. Are there any other tricks I'm missing?
Kyle Miller (Nov 16 2023 at 06:06):
Here's a trick I've been meaning to contribute eventually:
import Mathlib
open Lean Elab Meta Term Command Qq
open CategoryTheory
@[inherit_doc mkAppN]
macro_rules
| `(mkAppN $f #[$xs,*]) =>(xs.getElems.foldlM (fun x e => `(Expr.app $x $e)) f : MacroM Term)
-- Given an expression of the composition `f ≫ g`, return `f` and `g`.
def Lean.Expr.matchCategoryStructComp (e : Expr) : MetaM (Expr × Expr) :=
match e with
| mkAppN (.const ``CategoryStruct.comp [_v, _u]) #[_obj, _inst, _X, _Y, _Z, f, g] =>
return (f, g)
| _ => failure
elab "foo" t:term : command => Elab.Command.runTermElabM fun _ => do
let e ← withSynthesize <| Elab.Term.elabTerm t none
let e ← instantiateMVars e
let (f,g) ← e.matchCategoryStructComp
logInfo m!"{f}, {g}"
variable (C : Type*) [Category C] (X Y Z : C) (f : X ⟶ Y) (g : Y ⟶ Z)
foo f ≫ g
This way mkAppN
is both a function and a pattern (so long as the pattern uses an explicit array).
Kyle Miller (Nov 16 2023 at 06:06):
Without that trick, you can also do
def Lean.Expr.matchCategoryStructComp (e : Expr) : MetaM (Expr × Expr) :=
match e.getAppFnArgs with
| (``CategoryStruct.comp, #[_obj, _inst, _X, _Y, _Z, f, g]) =>
return (f, g)
| _ => failure
Adam Topaz (Nov 16 2023 at 14:13):
Thanks Kyle! That is indeed a nice trick. And I should have remembered about getAppFnArgs
as I’ve used it in the past.
Eric Wieser (Nov 16 2023 at 14:39):
Here's the Qq version:
def Lean.Expr.matchCategoryStructComp (e : Expr) : MetaM (Expr × Expr) := do
let ⟨Level.succ _,
~q(@Quiver.Hom _ (@CategoryStruct.toQuiver $C $instCat) $X $Y),
~q(@CategoryStruct.comp _ _ _ $Y _ $f $g)⟩ ← inferTypeQ e | failure
return (f, g)
Eric Wieser (Nov 16 2023 at 14:40):
inferTypeQ
is pretty much always the Qq tool you need if you started with an Expr
, though unfortunately the match syntax fights you every step of the way
Adam Topaz (Nov 16 2023 at 15:34):
Thanks @Eric Wieser . Yes, that works, but do you know of any way to do this without every typing the character @
? Ideally I would like to just use the category theoretic notation \hom
and \gg
Eric Wieser (Nov 16 2023 at 15:41):
You'd need to somehow turn on the "turn instance arguments into metavariables" flag, which I don't remember the spelling of
Eric Wieser (Nov 16 2023 at 15:42):
We have that for apply
, right?
Adam Topaz (Nov 16 2023 at 15:44):
oh maybe.
Adam Topaz (Nov 16 2023 at 15:44):
let me try something out
Eric Wieser (Nov 16 2023 at 15:46):
I think Qq does need fixing here so that what you want to write just works
Adam Topaz (Nov 16 2023 at 15:47):
I mean, I want to be able to write
def Lean.Expr.matchCategoryStructComp' (e : Expr) : MetaM (Expr × Expr) :=
match e with
| ~q($f ≫ $g) => return (f,g)
| _ => failure
Adam Topaz (Nov 16 2023 at 15:48):
But maybe that's too optimistic
Eric Wieser (Nov 16 2023 at 15:48):
I think you should still be forced to write inferTypeQ
, but you should be allowed to put underscores almost everywhere and drop all the @
s
Adam Topaz (Nov 16 2023 at 15:53):
This works, but it's quite verbose:
def Lean.Expr.matchCategoryStructComp (e : Expr) : MetaM (Expr × Expr) := do
let u ← mkFreshLevelMVar
let v ← mkFreshLevelMVar
let C : Q(Type u) ← mkFreshExprMVarQ q(Type u)
let _inst : Q(Category.{v} $C) ← mkFreshExprMVarQ q(Category.{v} $C)
let X : Q($C) ← mkFreshExprMVarQ q($C)
let Y : Q($C) ← mkFreshExprMVarQ q($C)
let Z : Q($C) ← mkFreshExprMVarQ q($C)
let f : Q($X ⟶ $Y) ← mkFreshExprMVarQ q($X ⟶ $Y)
let g : Q($Y ⟶ $Z) ← mkFreshExprMVarQ q($Y ⟶ $Z)
let _h ← assertDefEqQ q($f ≫ $g) e
return (f,g)
elab "foo" t:term : command => Elab.Command.runTermElabM fun _ => do
let e ← withSynthesize <| Elab.Term.elabTerm t none
let e ← instantiateMVars e
let (f,g) ← e.matchCategoryStructComp
logInfo m!"{f}, {g}"
variable (C : Type*) [Category C] (X Y Z : C) (f : X ⟶ Y) (g : Y ⟶ Z) (h : Z ⟶ Z)
foo f ≫ g
Adam Topaz (Nov 16 2023 at 15:54):
and also the infoview craps out near the let _h
line.
Eric Wieser (Nov 16 2023 at 15:55):
Eric Wieser said:
We have that for
apply
, right?
Do you remember the spelling of this "turn TC into subgoals" version of apply?
Adam Topaz (Nov 16 2023 at 15:56):
No, I don't
Eric Wieser (Nov 16 2023 at 15:57):
Do you have any memory of something like it existing, or am I imagining?
Adam Topaz (Nov 16 2023 at 15:58):
It existed in Lean3 for sure as apply_with ... { instances := false }
, but I've never used something similar in Lean4.
Adam Topaz (Nov 16 2023 at 16:00):
Aha I think it's apply (config := { allowSynthFailures := true })
.
Adam Topaz (Nov 16 2023 at 16:02):
And it looks like this is just part of apply
itself now: https://github.com/leanprover-community/mathlib4/blob/52199f53d887e390b8433f0866c1bad71d571357//Mathlib/Tactic/ApplyWith.lean#L7-L13
Eric Wieser (Nov 16 2023 at 16:04):
I was hoping this would lead to some kind of no_synth% foo x y
elaborator that would just work out of the box with Qq, but I don't see it
Adam Topaz (Nov 16 2023 at 16:05):
Oh that’s a good idea though
Eric Wieser (Nov 16 2023 at 16:06):
I certainly think that the current "failed to synthesize instance" behavior of Qq is a good thing, just not all the time
Eric Wieser (Nov 16 2023 at 16:06):
E.g. if I'm matching nat addition, I probably want it to be defeq to the real instance
Adam Topaz (Nov 16 2023 at 16:24):
Adam Topaz said:
and also the infoview craps out near the
let _h
line.
This error is strange.... does anyone understand why this happens? I can't find any way around it.
Adam Topaz (Nov 16 2023 at 16:24):
Error updating: Error fetching goals: Rpc error: InternalError: failed. Try again.
Scott Morrison (Nov 17 2023 at 02:07):
@Kyle Miller, could we please have your macro trick PR'd? It is life changing. :-) I would love it in Std or even Lean.
Adam Topaz (Nov 17 2023 at 02:08):
I’ll second that!
Last updated: Dec 20 2023 at 11:08 UTC