Zulip Chat Archive

Stream: mathlib4

Topic: making a TacticM


Heather Macbeth (Jan 22 2023 at 06:31):

I hope we can consider the #mathlib4 stream an acceptable location for simple mathematically-inspired tactic questions.

Question: How can I make a TacticM which performs simp only [one_mul, neg_mul]?

Mario Carneiro (Jan 22 2023 at 06:32):

evalTactic `(simp only [one_mul, neg_mul]) is the easy way

Heather Macbeth (Jan 22 2023 at 06:33):

I've also encountered evalTactic (←`(tactic| ring)) in mathlib4:
https://github.com/leanprover-community/mathlib4/blob/3fbdae49475528e1e4014189f98d217e67ece6da/Mathlib/Tactic/Linarith/Datatypes.lean#L322
That's similar, right?

Mario Carneiro (Jan 22 2023 at 06:41):

that's what I meant (making MWEs takes a while)

Mario Carneiro (Jan 22 2023 at 06:42):

import Mathlib.Algebra.Ring.Defs

open Lean Elab Tactic

elab "foo" : tactic => do
  evalTactic ( `(tactic| simp only [one_mul, neg_mul]))

example [Ring α] (x : α) : -1 * x = -x := by foo

Heather Macbeth (Jan 22 2023 at 06:43):

Ah :-)

Heather Macbeth (Jan 22 2023 at 06:44):

Isn't there usually a TacticM version of a tactic, though? I had the impression that it was considered good style to separate out the frontend/syntax from the rest, and that in a situation like this there was some prewritten TacticM simp which I would use.

Mario Carneiro (Jan 22 2023 at 06:47):

Every tactic is a TacticM. The good style is to make a MetaM version of the tactic, but support for that depends on the tactic and for a lot of things from core you still basically need a syntax on hand unless you want to bypass / redo a lot of the front end work

Mario Carneiro (Jan 22 2023 at 06:48):

calling simp-the-TacticM looks almost the same:

elab "foo" : tactic => do
  evalSimp ( `(tactic| simp only [one_mul, neg_mul]))

example [Ring α] (x : α) : -1 * x = -x := by foo

Mario Carneiro (Jan 22 2023 at 06:49):

you still need to pass it a syntax in this form

Heather Macbeth (Jan 22 2023 at 06:51):

It seems a bit wordy. Am I working against intended use in some way? Would it be more compact if there were a simp-the-MetaM for this?

Mario Carneiro (Jan 22 2023 at 06:53):

oh if you think this is wordy you don't want to see what calling simp the MetaM looks like

Heather Macbeth (Jan 22 2023 at 06:57):

Ok, I'll run with this. If I want a tactic which chains several things together (something I would have written in Lean 3 as

`[try { tac1 }, try { tac2 }, tac3, try { tac4 }]

) would it be better style to write it as

evalTactic ( `(tactic| try { tac1 } ; try { tac2 } ; tac3 ; try { tac4 })

or

do
evalTactic ( `(tactic| try { tac1 })
evalTactic ( `(tactic| try { tac2 })
evalTactic ( `(tactic| tac3)
evalTactic ( `(tactic| try { tac4 })

Heather Macbeth (Jan 22 2023 at 06:58):

And if I need to split it across multiple lines where do I put the linebreaks?

Mario Carneiro (Jan 22 2023 at 06:58):

Here's the no-stx version of calling simp:

elab "foo" : tactic => withMainContext do
  let simpTheorems  (simpOnlyBuiltins ++ [``one_mul, ``neg_mul])
    |>.foldlM (·.addConst ·) ({} : SimpTheorems)
  let ctx := { simpTheorems := #[simpTheorems], congrTheorems :=  getSimpCongrTheorems }
  _  simpLocation ctx none (.targets #[] true)

example [Ring α] (x : α) : -1 * x = -x := by foo

Heather Macbeth (Jan 22 2023 at 06:59):

what's a stx?

Mario Carneiro (Jan 22 2023 at 07:00):

it's actually not as bad as I thought it would be, this is obtained by inlining evalSimp, mkSimpContext, elabSimpArgs with your particular invocation arguments

Mario Carneiro (Jan 22 2023 at 07:01):

stx short for Syntax meaning the part where we construct the expression `(tactic| simp only [one_mul, neg_mul]) only to parse it for the actual arguments to pass to the backend

Mario Carneiro (Jan 22 2023 at 07:03):

Heather Macbeth said:

Ok, I'll run with this. If I want a tactic which chains several things together (something I would have written in Lean 3 as

`[try { tac1 }, try { tac2 }, tac3, try { tac4 }]

) would it be better style to write it as

evalTactic ( `(tactic| try { tac1 } ; try { tac2 } ; tac3 ; try { tac4 })

or

do
evalTactic ( `(tactic| try { tac1 })
evalTactic ( `(tactic| try { tac2 })
evalTactic ( `(tactic| tac3)
evalTactic ( `(tactic| try { tac4 })

Personally I like to do as much control flow stuff as possible in the do block rather than in the syntax, so I would write it like

do
  try evalTactic ( `(tactic| tac1)) catch _ => pure ()
  try evalTactic ( `(tactic| tac2)) catch _ => pure ()
  evalTactic ( `(tactic| tac3))
  try evalTactic ( `(tactic| tac4)) catch _ => pure ()

(assuming I didn't have any better way to call tac1-4)

Mario Carneiro (Jan 22 2023 at 07:04):

but either way works

Mario Carneiro (Jan 22 2023 at 07:05):

The main thing to keep in mind is that tac1; tac2 is not actually a tactic (it is a sequence of tactics), so I don't think `(tactic| tac1; tac2) will parse, you have to write `(tactic| (tac1; tac2))

Heather Macbeth (Jan 22 2023 at 07:07):

In this case tac2 is abel_nf. I found abelNFTarget which is nearly a TacticM except it needs an AbelNF.Config argument. Is there a quick way to make an empty one on the fly?

Mario Carneiro (Jan 22 2023 at 07:07):

{} probably

Heather Macbeth (Jan 22 2023 at 07:07):

Yup! Thanks!

Thomas Murrills (Feb 05 2023 at 11:18):

similar question along these lines: let's say I'm in TacticM and want to use `(tactic| ...) syntax to construct a term of a known type (which is not the main goal). What's the "best" pattern for that? evalTactic doesn't seem to easily let me assign a metavariable without some mvar-shuffling via run.

Is there a version (or a standard workflow) for evaluating tactics to construct a term? (I tried elabTermEnsuringType, but I ran into issues—I'm guessing because it couldn't see the tactic state? I wasn't sure, though.)

Jannis Limperg (Feb 06 2023 at 14:18):

elabTermEnsuringType is definitely the idiomatic solution. Turning a Syntax into an Expr is exactly what elaboration does.

Dan Velleman (Feb 06 2023 at 14:56):

It's a minor point, but I believe `(tactic| tac1; tac2) does parse. For example, a tactic I wrote includes this line:

evalTactic ( `(tactic| refine $hid ?_; clear $hid))

And it works fine.


Last updated: Dec 20 2023 at 11:08 UTC