Zulip Chat Archive

Stream: Is there code for X?

Topic: Extend getParamNames


Yicheng Tao (Nov 18 2024 at 12:29):

I was working on extracting the arg array of a constant. I found the function getParamNames in the library.

/-- Return the parameter names for the given global declaration. -/
def getParamNames (declName : Name) : MetaM (Array Name) := do
  forallTelescopeReducing ( getConstInfo declName).type fun xs _ => do
    xs.mapM fun x => do
      let localDecl  x.fvarId!.getDecl
      return localDecl.userName

I was trying to make it return not only the userName, but also the type. But the types that depending on local variable appears in the form like _uniq.3 if I simply use localDecl.type.

How to do it correctly?

Eric Wieser (Nov 18 2024 at 13:02):

Can you un- #xy ?

Yicheng Tao (Nov 18 2024 at 13:24):

I have the fullname of a constant and an Environment. What I want to get is the arg array of that constant.

E.g.:

theorem test {P Q : Prop} (p : P) : P := p

I have the fullname test and the environment. I want the desired function getParam to return the arg array as #[(P, Prop), (Q, Prop), (p, P)]. The signature of the function would be Name -> Array (String × String).

Daniel Weber (Nov 19 2024 at 04:49):

If you want to obtain a string you should take a look at docs#Lean.PrettyPrinter.Delaborator.delabConstWithSignature

Yicheng Tao (Nov 19 2024 at 04:59):

Daniel Weber said:

If you want to obtain a string you should take a look at docs#Lean.PrettyPrinter.Delaborator.delabConstWithSignature

I found doc-gen4 also use delaborator, but it will exceed time limit for some constants. I'm looking for some other way.

Daniel Weber (Nov 19 2024 at 05:01):

I don't think there are other ways to convert an Expr to a String (assuming you don't want to output the whole expression), perhaps you can find the Syntax where it's defined, but that'll be problematic with variable, local notation, etc.

Yicheng Tao (Nov 19 2024 at 06:04):

Too bad. I didn't expect it to be that complicated.

Daniel Weber (Nov 19 2024 at 06:05):

Why does the delaborator time out? Do you have a #mwe?

Yicheng Tao (Nov 19 2024 at 06:38):

It's hard to extract a #mwe from my code. I was modifying the ExtractData.lean script from LeanDojo. It processes the files in Mathlib one by one. Here is some relevant code:

import Lean
import Lake

namespace DocGen

open Lean Widget Meta
/--
Stores information about a typed name.
-/
structure NameInfo where
  /--
  The name that has this info attached.
  -/
  name  : Name
  /--
  The pretty printed type of this name.
  -/
  type : CodeWithInfos
  deriving Inhabited


/--
An argument to a declaration, e.g. the `(x : Nat)` in `def foo (x : Nat) := x`.
-/
structure Arg where
  /--
  The pretty printed binder syntax itself.
  -/
  binder : CodeWithInfos
  /--
  Whether the binder is implicit.
  -/
  implicit : Bool


/--
A base structure for information about a declaration.
-/
structure Info extends NameInfo where
  /--
  The list of arguments to the declaration.
  -/
  args : Array Arg
  deriving Inhabited

/--
Pretty prints a `Lean.Parser.Term.bracketedBinder`.
-/
private def prettyPrintBinder (stx : Syntax) (infos : SubExpr.PosMap Elab.Info) : MetaM Widget.CodeWithInfos := do
  let fmt  PrettyPrinter.format Parser.Term.bracketedBinder.formatter stx
  let tt := Widget.TaggedText.prettyTagged fmt
  let ctx := {
    env :=  getEnv
    mctx :=  getMCtx
    options :=  getOptions
    currNamespace :=  getCurrNamespace
    openDecls :=  getOpenDecls
    fileMap := default,
    ngen :=  getNGen
  }
  return Widget.tagCodeInfos ctx infos tt

private def prettyPrintTermStx (stx : Term) (infos : SubExpr.PosMap Elab.Info) : MetaM Widget.CodeWithInfos := do
  let fmt  PrettyPrinter.formatTerm stx
  let tt := Widget.TaggedText.prettyTagged fmt
  let ctx := {
    env :=  getEnv
    mctx :=  getMCtx
    options :=  getOptions
    currNamespace :=  getCurrNamespace
    openDecls :=  getOpenDecls
    fileMap := default,
    ngen :=  getNGen
  }
  return Widget.tagCodeInfos ctx infos tt

def Info.ofConstantVal (v : ConstantVal) : MetaM Info := do
  let e := Expr.const v.name (v.levelParams.map mkLevelParam)
  println! s!"{v.name}"
  -- Use the main signature delaborator. We need to run sanitization, parenthesization, and formatting ourselves
  -- to be able to extract the pieces of the signature right before they are formatted
  -- and then format them individually.
  let (sigStx, infos)  PrettyPrinter.delabCore e (delab := PrettyPrinter.Delaborator.delabConstWithSignature)
  let sigStx := (sanitizeSyntax sigStx).run' { options := ( getOptions) }
  let sigStx  PrettyPrinter.parenthesize PrettyPrinter.Delaborator.declSigWithId.parenthesizer sigStx
  let `(PrettyPrinter.Delaborator.declSigWithId| $_:term $binders* : $type) := sigStx
    | throwError "signature pretty printer failure for {v.name}"
  let args  binders.mapM fun binder => do
    let fmt  prettyPrintBinder binder infos
    return Arg.mk fmt (!binder.isOfKind ``Parser.Term.explicitBinder)
  let type  prettyPrintTermStx type infos
  return {
    toNameInfo := { name := v.name, type },
    args,
  }
end DocGen

...

/--
Extract premise information from `TermInfo` in `InfoTree`.
-/
private def visitTermInfo (ti : TermInfo) (env : Environment) : TraceM Unit := do
  let some fullName := ti.expr.constName? | return ()
  let fileMap  getFileMap
  let some const := env.constants.find? fullName | return ()
  let info  withEnv env $ DocGen.Info.ofConstantVal const.toConstantVal
  let args := info.args.map (fun arg => arg.binder.pretty)
  let type := info.type.pretty
  ...

This is only part of the code. The process visits every term in that file. I just try to print the constant which caused the time out, but I found that it succeeds when be processed individually. I guess the delaborator's time may be accumulated when processing the whole file?

Yicheng Tao (Nov 19 2024 at 06:39):

I wonder how to set the maxHeartbeats to be higher. The default is 200000 and I failed to set it with set_option.

Yicheng Tao (Nov 19 2024 at 06:56):

Here is the error message:

uncaught exception: (deterministic) timeout at `delab`, maximum number of heartbeats (200000) has been reached
Use `set_option maxHeartbeats <num>` to set the limit.(invalid MessageData.lazy, missing context)

Damiano Testa (Nov 19 2024 at 06:59):

What is the error when you try, say set_option maxHeartbeats 0?

Yicheng Tao (Nov 19 2024 at 07:04):

set_option maxHeartbeats doesn't work. The scripts is a independent file in the project and is ran by using lake env lean --run.

Yicheng Tao (Nov 19 2024 at 07:04):

It seems that this option should be passed in another way.

Damiano Testa (Nov 19 2024 at 07:06):

You should place it in the file where you want it to take effect and its effect start from the set_option line.

Damiano Testa (Nov 19 2024 at 07:07):

You can limit it to the next declaration via set_option ... in <command>.

Yicheng Tao (Nov 19 2024 at 07:10):

Oh I found the place where I can modify the config.

Yicheng Tao (Nov 19 2024 at 07:10):

For someone who may be interested:

let (trace, _)  traceM.run'.toIO {fileName := s!"{path}", fileMap := FileMap.ofString input, maxHeartbeats := 2000000000} {env := env}

Yicheng Tao (Nov 19 2024 at 07:11):

And it's weird that the 2000000000 here seems to be 2000000 in error message.

Damiano Testa (Nov 19 2024 at 07:17):

There are two "levels" of heartBeats: an "internal" one that is more fine-grained and a "user-facing" one that is <internal>/10^3.


Last updated: May 02 2025 at 03:31 UTC