Zulip Chat Archive

Stream: lean4

Topic: How to print shadowed declarations?


Eric Rodriguez (Mar 11 2024 at 20:05):

Take this code:

structure Box where
  a : Nat
  b : Nat
deriving BEq

#print instBEqBox -- { beq := beqBox✝ }

Turning on pp.sanitizeNames does let you access the underlying name of this, but this changes on the ngen and such like so we can't actually access it. It'd be really nice to print it so that I could modify the instance generated by deriving without having to rewrite the scaffolding.

Eric Rodriguez (Mar 12 2024 at 00:34):

I was told about open private here but I just can't get it to work. I am told that there is private declarations, but can't seem to figure out the syntax to actually do the opening.

Mario Carneiro (Mar 12 2024 at 18:18):

this is not a private declaration (well it is but that's not the problem because you are still in the same module as the declaration), it is a declaration with an inaccessible name. You can call #print if you construct the name manually:

import Lean
open Lean

structure Box where
  a : Nat
  b : Nat
deriving BEq

set_option pp.sanitizeNames false in
#print instBEqBox
-- def instBEqBox : BEq Box :=
-- { beq := beqBox.22 }

run_cmd Elab.Command.elabCommand <|←
  `(#print $(mkIdent (addMacroScope ( getMainModule) `beqBox 22)))
-- private def beqBox._@.Mathlib.Test._hyg.22 : Box → Box → Bool :=
-- fun x x_1 ↦
--   match x, x_1 with
--   | { a := a, b := a_1 }, { a := b, b := b_1 } => true && a == b && a_1 == b_1
--   | x, x_2 => false

Mario Carneiro (Mar 12 2024 at 18:21):

(The fact that several deriving instances produce global declarations with inaccessible names is a recurring source of issues, and I've been planning to write a PR to make them accessible for some time. For example this causes an issue if you want to prove that an autogenerated Ord or BEq instance is lawful.)

Eric Rodriguez (Mar 12 2024 at 18:25):

Thanks! This really does seem quite annoying. One thing I've been meaning to figure out is macro scopes - what are they and how should I use them?

Robert Maxton (Mar 12 2024 at 22:18):

They're the scope of a macro :p. That is, the 'jurisdiction' of a macro over names and definitions. When a new macro is begun (controlled I think primarily withNewMacroScope?), it gets to include its macro scope (which is apparently implemented as just a unique number, probably encoding nesting of macros?) into the name of all (parsing) variables it introduces; this enforces hygiene requirements.

Robert Maxton (Mar 12 2024 at 22:18):

ah, yeah, here: docs#Lean.MacroScope

Robert Maxton (Mar 12 2024 at 22:22):

So, in theory, you should introduce a new macro scope whenever there might be danger of name collision of parse variables. In practice I think it's not much used/handled for you when writing notation or simple macro_rules and elab_rules, but more involved elaborators pretty much all start with withNewMacroScope do, and also if you might collide with yourself (through a recursive call, for example) that should get a withNewMacroScope do as well.

Eric Rodriguez (Mar 12 2024 at 22:37):

Ahh, so that number 22 is a macro-scope in this case. It does seem odd to me because I feel like no macros have been involved here, but I guess (a) most things are macros in some way or another and (b) it's probably the macro scope of the deriving stuff. [Not sure whether multiple derivings would have same or different macro scopes, I guess it depends on the implementation...].

Kyle Miller (Mar 12 2024 at 22:56):

It's possible to make a command that prints the value after unfolding all inaccessible constants (i.e., the ones with macro scopes):

structure Box where
  a : Nat
  b : Nat
deriving BEq

open Lean Elab Command in
elab "#print_val " id:ident : command => do
  addCompletionInfo <| CompletionInfo.id id id.getId (danglingDot := false) {} none
  let c  resolveGlobalConstNoOverloadWithInfo id
  let some d := ( getEnv).find? c | throwError "unknown identifier '{mkConst c}'"
  let some val := d.value? | throwError "no value for decl"
  let val  liftTermElabM <| Meta.transform val
    (pre := fun e => do
      if let .const n .. := e.getAppFn' then
        if n.hasMacroScopes then
          if let some e'  Meta.unfoldDefinition? e then
            return .visit e'
      return .continue)
  logInfo m!"{val}"

#print_val instBEqBox
/-
{
  beq := fun x x_1 ↦
    (fun motive x x_2 h_1 h_2 ↦ Box.casesOn x fun a b ↦ Box.casesOn x_2 fun a_1 b_1 ↦ h_1 a b a_1 b_1) (fun x x ↦ Bool)
      x x_1 (fun a a_1 b b_1 ↦ true && a == b && a_1 == b_1) fun x x ↦ false }
-/

Last updated: May 02 2025 at 03:31 UTC