Zulip Chat Archive

Stream: metaprogramming / tactics

Topic: Meta4dummies


Moritz Doll (May 27 2023 at 21:02):

During the Banff workshop/my porting attempt for congrm I have started writing a list of important definitions for tactic writing, which now turned somewhat into a general introduction to tactic-writing geared towards mathematicians. Anne advised me to put this into the mathlib4-wiki and now it is here: https://github.com/leanprover-community/mathlib4/wiki/Metaprogramming-for-dummies
It is very far from finished, but I will probably not have too much time to work on this until next weekend. Additions and corrections are very welcome. Be careful, I now very little about metaprogramming, so there are probably lots of wrong things already (and more to come).

Shreyas Srinivas (May 28 2023 at 09:43):

Suggestion: Some examples with v/s without Qq

Damiano Testa (Jun 08 2023 at 16:37):

Inspired by this thread, I decided to start writing some examples. I am learning a little bit of parsing and writing a simple example along the way. If people think that it could be useful, I can add it to the wiki. Of course, comments are very welcome!

import Lean
open Lean Elab Tactic Term Meta
/-
This example shows how to take a user input `a` and add to the local context
the assumption `[a].length = 1`.

The underlying principle is that we want to take some user input, embed it
into the rich type-system that Lean has and then process it.

The following line of code, informs Lean that `single_me <something>` is a tactic.
It also makes Lean aware that `<something>` is a `term` (or, with all the
namespaces, a `Lean.Parser.Category.term`).
-/
syntax "single_me " term : tactic
/-
Thus, if we are in tactic mode and we write `single_me whatever`, then Lean
knows that this is a tactic and that `whatever` is a `term`.

Let's test this out!
-/
example : true := by
  single_me whatever  -- tactic 'tacticSingleMe_' has not been implemented
/-
Sure, Lean has no idea what the tactic is supposed to do.

However, if you remove `whatever`, leaving only `singleMe`, then the error
message changes to `expected term`: the tactic is not implemented, but Lean
knows that it should expect a `term`.
If you tried with something else, say `whoKnows`, Lean would have replied with
`unknown tactic`.

Let's implement the `single_me` tactic.
-/

-- these are the "elaboration rules" for a tactic
elab_rules : tactic
-- if Lean finds `single_me [stuff]`, it is going to assign the label `x`
-- to [stuff] and it is going to assume that it is a `term`.  This basically
-- affects what we can ask Lean to do with `x`.
| `(tactic| single_me $x:term) => do
  -- at this point, the tactic state contains ``x: TSyntax `term``:
  -- `x` "entered" the local context of our tactic development as a
  -- "Typed Syntax" with `Category` `term`.
  -- The next line essentially runs `have : [$x].length = 1 := by simp` in
  -- the local context of where our tactic will be called.
  evalTactic ( `(tactic| have : [$x].length = 1 := by simp))
  -- Note that we are using `$x` to refer to the "quoted" variable `x`.
  -- This is "our" input: we are going to type it in tactic mode, Lean
  -- parses it and performs actions with it.

example : True := by
  single_me 0  -- local context now contains `this: List.length [0] = 1`!
  trivial

/-
Great!  We managed to add to the local context the hypothesis that the
list consisting of `0` has length 1.

In a similar vein, here are more examples of our new tactic:
-/

example {α : Type} [Add α] {a b : α} : True := by
  single_me a
  -- local context now contains `this: List.length [a] = 1`
  single_me 1 + 2
  -- local context now contains `this: List.length [1 + 2] = 1`
  single_me a + b
  -- local context now contains `this: List.length [a + b] = 1`
  single_me [a]
  -- local context now contains `this: List.length [[a]] = 1`
  single_me  x : Int, x + 5 = 0
  -- local context now contains `this: List.length [∀ x : Int, x + 5 = 0] = 1`
  trivial

/-
As you can see, the `term` Category is fairly flexible: basically, anything
that looks like an `Expr`ession will be allowed.

If we had used `ident` instead of `term`, then we would have been allowed
to use "identifiers", essentially the variables in our context.  If we did
that, then in the final example `single_me a` would have worked, but
nothing else would have.
-/

Last updated: Dec 20 2023 at 11:08 UTC