Zulip Chat Archive
Stream: new members
Topic: Grab the nth argument of a function call via metaprogramming
Malcolm Langfield (Nov 25 2023 at 11:43):
I am guessing there's some basic type theoretic reason why this is impossible, but I am trying to pattern match on a specific function to extract an argument out a function call (with default). Here is a MWE:
import Mathlib
@[match_pattern]
def f (a b : ℕ) : ℕ := a + b
def g : ℕ → ℕ
| f a b => a
| n => n
I get the following error: ■ invalid patterns,
a is an explicit pattern variable, but it only occurs in positions that are inaccessible to pattern matching .(Nat.add a b)
Is anything like this possible without adding a constructor to the underlying type? Even with metaprogramming?
Kevin Buzzard (Nov 25 2023 at 11:44):
f 1 3 = f 2 2 = 4, so what do you expect g 4 to be?
Malcolm Langfield (Nov 25 2023 at 11:46):
I expect g 4 = 4
, so I guess this reveals the issue. It requires that the argument to g
is not evaluated until after the call to g
, or something vaguely like that?
Malcolm Langfield (Nov 25 2023 at 11:47):
In fact the value of the function changes depending on whether the expression passed to it is simplified or not. But this is precisely the behavior I want. Would it be possible to write a tactic combinator that does this?
I suppose this means that if one were to write a thing that does this, it would not be a function in the mathematical sense, but maybe more like a parser.
Malcolm Langfield (Nov 25 2023 at 13:33):
Indeed this can be done.
def f (a b : Nat) : Nat := a + b
open Lean.Elab.Tactic in
elab "extract_first_argument " n:ident " : " v:term " giving the aforementioned argument the name " name:ident : tactic =>
let a :=
match v with
| `(f $a:term $_:term) => a
| _ => v
withMainContext do
evalTactic <| ← `(tactic| generalize $n : $a = $name)
example : f 3 4 = 7 := by
extract_first_argument eq : f (1 + 2 + 3 + 4) 4 giving the aforementioned argument the name a
simp only
Adam Topaz (Nov 25 2023 at 15:03):
You can even make a term elaborator that does this:
import Lean
open Lean Elab Term
elab "last_arg%" t:term : term => do
match ← elabTerm t none with
| .app _ f => return f
| _ => throwError "Not a function call."
example (f : Nat → Nat → Nat) : (last_arg% (f 1)) = 1 := rfl
Adam Topaz (Nov 25 2023 at 15:05):
This doesn't do exactly what you want since it gets the last argument, not the first one, but you get the idea
Adam Topaz (Nov 25 2023 at 15:09):
Here's a variant that gets the first arg:
elab "first_arg%" t:term : term => do
match (← elabTerm t none).getAppFnArgs with
| (_, .mk (a :: _)) => return a
| _ => throwError "Failed"
Malcolm Langfield (Nov 25 2023 at 18:04):
Oh this is extremely nice! Thanks so much, I will probably use this instead! :heart:
Last updated: Dec 20 2023 at 11:08 UTC