Zulip Chat Archive

Stream: lean4

Topic: Tactic by manipulating Expr


Tomas Skrivan (Sep 23 2021 at 16:54):

If I have a function Expr -> MetaM (Expr × Expr) that turns an expression to an another expression and also provide a proof that they are equal. How do I turn this into a tactic that rewrites the goal?

Also, what if I have only Expr -> MetaM Expr but can also generate tactic proof of that the output is equal to the input. How can I make a tactic to rewrite the goal?

I had a rough idea how to do it in lean3 but in lean4 I'm stomped.

Jakob von Raumer (Sep 23 2021 at 17:24):

First use that function in a function MVarId -> MetaM MVarId which returns a new MVar with the new goal, assigns the input MVar with the result of substituting with the equality and assigning the substituted MVar with the new MVar.
Then you need to define a Tactic which calls that function and sets the main goal to its output.

Tomas Skrivan (Sep 23 2021 at 18:58):

Thanks! It pointed me to the right direction.

Actually, the function more or less exists already Lean.Meta.replaceTargetEq :)

I still need to figure out what is the best way to create the equality proof(its Expr representation) if I know the tactic proof for it.

Chris B (Sep 23 2021 at 19:07):

There are some examples in the current version of mathlib4's normNum, particularly here. https://github.com/leanprover-community/mathlib4/blob/7ac569847b94980d3d668ae6079131f5cd2fe559/Mathlib/Tactic/NormNum.lean#L104

Tomas Skrivan (Sep 23 2021 at 20:09):

I think, I'm on the right track, but running into problems with universes(something I do not really understand)

I'm trying to write a simple tactic that replaces the current goal X with two new goals X = id X and id X. The first goal X = id X should be solvable by simp but I'm getting an error incorrect number of universe levels id

import Lean
import Lean.Meta.Basic
import Lean.Elab.Tactic.Basic

open Lean
open Lean.Meta
open Lean.Elab.Tactic

syntax (name := mytactic) "mytactic" : tactic

def mytacticCore (mvarId : MVarId) : MetaM (List MVarId) :=
  withMVarContext mvarId do
    let tag       getMVarTag mvarId
    let target    getMVarType mvarId
    let u         getLevel target
    let targetNew := (mkApp (mkConst `id) target)
    let mvarNew   mkFreshExprSyntheticOpaqueMVar targetNew tag
    let eq        mkEq target targetNew
    let eqMvar   mkFreshExprSyntheticOpaqueMVar eq tag
    let val  := mkAppN (Lean.mkConst `Eq.mpr [u]) #[target, targetNew, eqMvar, mvarNew]
    assignExprMVar mvarId val
    return [eqMvar.mvarId!, mvarNew.mvarId!]


@[tactic mytactic] def tacticMytactic : Tactic
| _ => do
          let mainGoal  getMainGoal
          let todos  mytacticCore mainGoal
          setGoals todos
          pure ()

theorem test (X : Type) (x : X) : id x = x :=
  by
    mytactic
    simp

Chris B (Sep 23 2021 at 20:36):

id needs a universe. Without looking too careully at it I would assume you can just use u here:

let targetNew := (mkApp (mkConst `id [u]) target)

Chris B (Sep 23 2021 at 20:39):

If you print a declaration and it has a little .{u} or something else tacked onto the end, that means it takes one or more (one in this case) universe parameters in this context.

#print id
-- def id.{u} : {α : Sort u} → α → α := ..

Tomas Skrivan (Sep 23 2021 at 21:00):

Thanks! Makes sense. I just recalled from "Lean Together 2021: Metaprogramming in Lean 4" that there is mkAppM that deals will all of this for me. So switching two lines to:

    let targetNew  mkAppM `id #[target]
    let val   mkAppM `Eq.mpr #[eqMvar, mvarNew]

Makes it work and I do not have to deal with universes and implicit arguments.

Chris B (Sep 23 2021 at 21:41):

Nice, that does seem handy.


Last updated: Dec 20 2023 at 11:08 UTC