Zulip Chat Archive
Stream: lean4
Topic: Running CommandElabM inside IO and installing instances
Gauvain Devillez (Apr 25 2024 at 12:15):
Hello,
I am trying to reuse a function from a command_elab which creates an instance of Cow
and installs an instance of the type class Age
through metaprogramming. Because I want the function to be used in main, I would like to convert the CommandElabM
into IO
. I can obtain the constructed instance, but the type classes are lost.
In the example below, the age
function works and the Age
instance is found when using the command, but the Age
is not found when using the function convertCow
.
I suspect it has to do with the Enviroment
but could not find how to obtain the current Enviroment
from IO
. Is there a way?
Sorry if it is unclear.
import Lean
import Qq
open Qq Lean Elab Command
-- A structure
structure Cow where
name : String
-- A typeclass
class Age (c : Cow) : Type :=
age : Nat
-- Some function using the typeclass
def Cow.age (c : Cow) [b : Age c] : Nat :=
b.age
-- Function from Mario Carneiro on zulip : https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/read.20term.20from.20file/near/400316661
-- I do not know if it will preserve the side-effects of installing the instances.
def runCommandElabM (env : Environment) (x : CommandElabM α) : IO α := do
let cmdCtx := {
fileName := "<empty>"
fileMap := .ofString ""
tacticCache? := none
}
match (← liftM <| EIO.toIO' <| (x cmdCtx).run { env, maxRecDepth := maxRecDepth.defValue }) with
| .ok (a, _) => return a
| .error e =>
throw <| IO.Error.userError s!"unexpected internal error: {← e.toMessageData.toString}"
-- Function to construct a Cow and install an Age instance in metaprogramming.
unsafe def makeCowAux (ident : Name) (name : String) (age : Nat) : Elab.Command.CommandElabM Cow := do
-- We create the Cow
have cowQ : Q(Cow) := q({name := $name})
Elab.Command.liftCoreM <| addAndCompile <| .defnDecl {
name := ident
levelParams := []
type := q(Cow)
value := q($cowQ)
hints := .regular 0
safety := .safe
}
setReducibleAttribute ident
have cow : Q(Cow) := mkConst ident []
-- We create the Age object
have ageQ : Q(Age $cow) := q({age := $age})
have ageName : Name := Name.mkStr ident "ageClass"
Elab.Command.liftCoreM <| addAndCompile <| .defnDecl {
name := ageName
levelParams := []
type := q(Age $cow)
value := ageQ
hints := .regular 0
safety := .safe
}
-- We install the instance
Elab.Command.liftTermElabM <| Meta.addInstance ageName .global 42
-- We want the object registered under the Name
evalConst Cow ident
-- We want to get out of CommandElabM and into IO
unsafe def convertCow (ident : String) (name : String) (age : Nat) : IO Cow := do
-- This part might be wrong. I did not find a way to get the current environment from outside the CommandElabM monad.
-- Also, I do not know what I should import exactly.
let env ← importModules #[`Init] {}
let cmdCow := makeCowAux ident name age
runCommandElabM env cmdCow
-- We want to be able to use the function in a command
syntax (name := makeCow) "make_cow" ident str num : command
@[command_elab makeCow]
unsafe def makeCowImpl : Elab.Command.CommandElab
| `(make_cow $ident $name $age) => do
let _ ← makeCowAux ident.getId name.getString age.getNat
| _ => Elab.throwUnsupportedSyntax
make_cow bessy "Bessy" 10
-- Used as a command, it works
#check bessy.ageClass
#eval bessy.age
unsafe def myFunction : IO Unit := do
let newBessy : Cow ← convertCow "newBessy" "newBessy" 42
IO.println newBessy.name
-- Used in a function, the instances are missing/not found.
IO.println newBessy.ageClass.age
IO.println newBessy.age
Sebastian Ullrich (Apr 25 2024 at 12:42):
What you try to do in myFunction
is not possible. You run convertCow
at run time of the function but somehow expect it to influence the compile time of the function, which would be time travel. Can you say more about what you're trying to achieve?
Sebastian Ullrich (Apr 25 2024 at 12:46):
It may be the case that you want to move the computation convertCow "newBessy" "newBessy" 42
to compile time by introducing a term elaborator that does something very similar to makeCowAux
but the context is not sufficiently clear
Gauvain Devillez (Apr 25 2024 at 12:56):
Thank you for the reply! I did not think about the compile time.
To give more context, we are trying to build graphs from json files along with their properties. For example, a graph and the property that it is connected. We do so using elab to have good performances (there will be tens of thousands of files).
What I want to do now is to be able to create an exe that would go through a list of json files and verify those properties for the graphs in each of them. I was hoping I would not have to duplicate the code.
Eric Wieser (Apr 26 2024 at 06:51):
I think you're looking for withImportModules, which starts an interpreter/compiler within your runtime
Last updated: May 02 2025 at 03:31 UTC