Zulip Chat Archive

Stream: metaprogramming / tactics

Topic: run meta program from command line


Christian Merten (Feb 24 2025 at 19:41):

I am trying to run some analysis on a directory containing .lean files, which all have .olean files already. The information that I want to extract from each file is contained in an environment extension. What I currently have is this (minimized of course, in particular I dropped the logic for extracting and processing the env extension state):

import Lean
open Lean Elab

def countDecls (ns : Array Name) : IO Unit := do
  for name in ns do
    let _  importModules #[name] {}
    -- query some information from an environment extension
    IO.println s!"queried information: ..."

def pathToModule (path : IO.FS.DirEntry) : Name :=
  let fp := (path.path.toString.replace "/" ".").replace ".lean" ""
  fp.toName

def countAllInDir (path : System.FilePath) : IO Unit := do
  let ns := ( path.readDir).map pathToModule
  countDecls ns

#eval countAllInDir "directory"

The problem is that if the directory contains more than a few (say 5) files, my computer runs out of memory. Monitoring the memory usage with top shows that memory usage is monotonically increasing on each processed file. So clearly, I am doing something wrong here. Do I need to free some memory allocated by importModules? Or is there a cheaper way to extract environment extension data from an .olean file?
(I looked into Shake/Main.lean which uses docs#Lean.readModuleData, but I have no idea how to extract environment extension data from that).

Kim Morrison (Feb 26 2025 at 03:18):

docs#Lean.withImportModules

Christian Merten (Feb 26 2025 at 10:59):

Thanks, that worked, after figuring out I have to manually complicate expressions so that they can escape withImportModules (concretely, I had name : Lean.Name queried from the environment which I had to turn into name.toString.toName).

Christian Merten (Jun 17 2025 at 13:33):

I am running into this again. What is the correct way to extract information from a withImportModules invocation? For simple things, such as name : Lean.Name the stupid name.toString.toName works, but for more complicated things I don't know how to trick Lean into creating a new memory reference.

A mwe:

import Lean
open Lean

unsafe def main : IO Unit := do
  _  initSearchPath ""
  let nm1  Lean.withImportModules #[`Lean] {} fun env => do
    return (env.constants.map₁.fold (init := #[]) fun r k _ => r.push k)[42]!.toString.toName
  IO.println s!"first name: {nm1}"
  let nm2  Lean.withImportModules #[`Lean] {} fun env => do
    return (env.constants.map₁.fold (init := #[]) fun r k _ => r.push k)[37]!
  IO.println s!"second name: {nm2}"

with corresponding section in lakefile.toml:

[[lean_exe]]
name = "bar"
root = "Bar"
supportInterpreter = true

yields on lake exe bar the output:

first name: Lean.Elab.Tactic.NormCast.evalConvNormCast._cstage2

and then aborts silently.

Jannis Limperg (Jun 17 2025 at 14:43):

Does it help if you do all your processing and printing inside the withImportModules? Also, note the new restriction about env extensions mentioned in docs#Lean.withImportModules; this may or may not cause problems for you.

Christian Merten (Jun 17 2025 at 14:49):

This would be equivalent to

    let env  importModules #[name] {}
    /- processing goes here -/

right? In this case, I can't do that, because the memory needs to be freed, otherwise my memory runs out if processing, say, more than 5 files.


Last updated: Dec 20 2025 at 21:32 UTC