Zulip Chat Archive
Stream: lean4
Topic: Migrating my custom tactic library from Lean 3
Tsuru (Jun 22 2023 at 08:03):
Lean 3 allows users to define some custom tactic monad and use it instead. Especially, begin[xxx] ... end
blocks use tactics not from tactic.interactive
but from xxx.interactive
, making it easy to switch between different sets of tactics.
I learned this in https://leanprover-community.github.io/archive/stream/113488-general/topic/no.20lean.20messages.20output.20in.20hacked.20mode.html and (if necessary) my own code is
https://github.com/ge9/ntac/blob/c34eceeeaee6957f716874a5482ae23be94bbab1/src/ntac/ntac.lean#LL56C2-L56C3 .
Now I'm wondering how to implement this in Lean4. Any help would be appreciated!
Floris van Doorn (Jun 22 2023 at 09:47):
Metaprogramming in Lean 4 is a good place to start to learn: https://github.com/arthurpaulino/lean4-metaprogramming-book
Tsuru (Sep 07 2023 at 02:58):
Now I have suceeded in this.
First, define the custom tactic monad, and provide conversion functions and a required instance definition for it.
abbrev NTacticM := StateT My_State TacticM
abbrev NTactic := Syntax → NTacticM Unit
def mylift {α} (m: TacticM α) : NTacticM α := fun x => do let res ← m; return (res, x)
def mylift_inv {α} (m: NTacticM α) : TacticM α := do let res ← m 0; return res.fst
instance [Monad m] [MonadRecDepth m] : MonadRecDepth (StateT α m) where
withRecDepth d x := fun ctx => MonadRecDepth.withRecDepth d (x ctx)
getRecDepth := fun n => do let r ← MonadRecDepth.getRecDepth; return (r, n)
getMaxRecDepth := fun n => do let r ← MonadRecDepth.getMaxRecDepth; return (r, n)
Then I copied two definitions from the standard library and modified like this, to use the attribute attr_ntac_tactic
instead of the existing tactic
. (this is highly undocumented and I don't know its validity, but it's working)
unsafe def mkNTacticAttribute : IO (KeyedDeclsAttribute NTactic) :=
mkElabAttribute NTactic `builtin_attr_ntac_tactic `attr_ntac_tactic `Ntac.Parser.Tactic `Ntac.Elab.Tactic.NTactic "ntac_tactic" `Ntac.Elab.Tactic.tacticElabAttribute
@[init mkNTacticAttribute] opaque tacticElabAttribute : KeyedDeclsAttribute NTactic
Then I redefined many existing parser and elab/eval functions, including byTactic
, elabByTactic
, tacticSeq
, evalTacticSeq
, tacticParser
, evalTactic
.
I modified tacticParser
to accept both existing tactic
s and my custom ntac_tactic
s. (ntac_tactic
is prioritized)
@[inline] def ntac_tacticParser (rbp : Nat := 0) : Parser :=
(categoryParser `ntac_tactic rbp) <|> (categoryParser `tactic rbp)
Now I can add custom tactic. This is custom exact
tactic that imitates the default exact
tactic, with a custom message.
syntax (name := ntac_exact) "exact " term : ntac_tactic
@[attr_ntac_tactic ntac_exact] def evalMyExact : Ntac.Elab.Tactic.NTactic := fun stx => do
Lean.logInfo "this is my exact"
Ntac.Elab.Tactic.mylift $ Lean.Elab.Tactic.evalTactic (stx.setKind `Lean.Parser.Tactic.exact)
I'll share the whole code at GitHub in the future, or soon if requested.
Last updated: Dec 20 2023 at 11:08 UTC