Zulip Chat Archive

Stream: lean4

Topic: Unit-testing a MetaM computation


Valentin Robert (Sep 05 2025 at 19:54):

I have a function Expr → MetaM SomeResult that I would like to test on sample expressions, but I'd like to build these expressions using human-readable syntax.

I am struggling to find a way to run TacticM computations in #eval. While I have found a way to use functions to get something that type checks, it fails at run-time, seemingly because of the bogus MVarId I am creating, even though I think my code doesn't depend on the "goal".

Here is where I am:

-- The function I am trying to test.
def someFunction (_e : Expr) : MetaM String := return "placeholder for real function"

-- Here is the wrapper that generates an Expr. In practice, I will want this to be within some context, but even the context-free version eludes me at the moment.
def testSomeFunction : TacticM String := do
  let e  elabTerm ( `(1 + (1 : Nat))) none
  someFunction e

-- And here is where I clumsily do my best to run the above TacticM... The crux is `Tactic.run` demands an `mvarId`, so I make a bogus one, but seemingly it's unhappy with it
#eval MetaM.run' (do
    let mvarId  mkFreshMVarId
    Term.TermElabM.run' $ Tactic.run mvarId do
      let s  testSomeFunction
      logInfo s
      return ()
  ) {} {}

The error is as follows:

unknown metavariable '?_uniq.19430'

How should I go about this?

Robin Arnez (Sep 05 2025 at 20:38):

mkFreshMVarId is the wrong function, you need to use docs#Lean.Meta.mkFreshExprMVar:

import Lean

open Lean Meta Elab Tactic

-- The function I am trying to test.
def someFunction (_e : Expr) : MetaM String := return "placeholder for real function"

-- Here is the wrapper that generates an Expr. In practice, I will want this to be within some context, but even the context-free version eludes me at the moment.
def testSomeFunction : TacticM String := do
  let e  elabTerm ( `(1 + (1 : Nat))) none
  someFunction e

-- And here is where I clumsily do my best to run the above TacticM... The crux is `Tactic.run` demands an `mvarId`, so I make a bogus one, but seemingly it's unhappy with it
#eval MetaM.run' (do
    let mvarId  mkFreshExprMVar none
    Term.TermElabM.run' $ Tactic.run mvarId.mvarId! do
      let s  testSomeFunction
      logInfo s
      return ()
  ) {} {}

Marcus Rossel (Sep 05 2025 at 20:38):

@Valentin Robert Do you need the TacticM monad to be involved? Or do you want to test functions just in MetaM?

Marcus Rossel (Sep 05 2025 at 20:40):

Not sure if this might cause problems in certain cases, but this also produces the desired output:

#eval Term.TermElabM.run' do
  let s  testSomeFunction { elaborator := .anonymous } |>.run' { goals := [] }
  logInfo s
  return ()

It's based on the implementation of Tactic.run.

Valentin Robert (Sep 06 2025 at 23:53):

Thanks, both answers helped!


Last updated: Dec 20 2025 at 21:32 UTC