Zulip Chat Archive
Stream: general
Topic: Get scope *before* some syntax is evaluated
Michael Rothgang (Jul 31 2024 at 18:49):
I'm writing a linter which inspects variable
commands. I'd like to know which variables were in scope before a particular variable command was executed. Is there a way to achieve this?
Simply looking at getScope
includes the effect of that command already. (This examples requires two files; probably, one file is also possible with further effort.)
import Batteries.Tactic.Lint
open Lean Elab Command
def badVariableLinter : Linter where run := withSetOptionIn fun stx => do
-- TODO: how to access the current scope *before* the `variable` command was parsed?
let previousVariablesOld := ((← getScope).varDecls).map fun var ↦ var.raw
-- This already prints current variable names...
dbg_trace previousVariablesOld
-- Now, the actual linter can do whatever
initialize addLinter badVariableLinter
-- In a new file, importing just this linter and containing "variable (a)", and observe that a is printed.
Michael Rothgang (Jul 31 2024 at 19:48):
There certainly is a small hack I could use: elaborate a bare section
command before stx
and using the not-current-anymore scope to compare. I'd be happy to learn about better solutions, though!
Damiano Testa (Jul 31 2024 at 20:08):
The variables in Scopes
(note the plural!) seem to remember each layer already:
import Lean
open Lean Elab Command
/-- `gs i` shows the available `variable`s `i` scopes ago (defaulting to 0 if not given). -/
elab "gs" i:(num)? : command => do
let sc := (← get).scopes
let i := (i.getD (← `(num|0))).getNat
dbg_trace "Number of available scopes: {sc.length}"
dbg_trace ((sc.getD i default).varDecls.map getBracketedBinderIds).flatten
variable (a0 : Nat)
section
variable (a1 : Nat)
section
variable (a2 : Nat)
section
variable (a3 : Nat)
section
-- try various numbers to see how you "climb up" the variables in scope
gs 2
Michael Rothgang (Jul 31 2024 at 21:01):
I know! But isn't this basically what I'm saying above? Put differently, using gs n
in
import Lean
open Lean Elab Command
/-- `gs i` shows the available `variable`s `i` scopes ago (defaulting to 0 if not given). -/
elab "gs" i:(num)? : command => do
let sc := (← get).scopes
let i := (i.getD (← `(num|0))).getNat
dbg_trace "Number of available scopes: {sc.length}"
dbg_trace ((sc.getD i default).varDecls.map getBracketedBinderIds).flatten
variable (a0 : Nat)
section
variable (a3 : Nat)
variable (a4 : Nat)
section
-- try various numbers to see how you "climb up" the variables in scope
gs 4
does not distinguish a3 and a4.
Damiano Testa (Jul 31 2024 at 21:07):
So you are trying to distinguish between variable (a b)
and variable a variable b
? I.e. if, within a given scope, whether the variables were added in separate variable commands or in the same?
Michael Rothgang (Aug 01 2024 at 09:05):
The case I'm really interested in is variable {a} variable (a)
(two separate commands, the binder type of a
is changed in the second one).
Last updated: May 02 2025 at 03:31 UTC