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