A monotonic counter for generating hygienic fresh names, needed wherever a pass introduces a
variable that must not collide with anything a user could have written β used by expression
desugaring's tuple-pattern/multi-binder-collapse transformations, and by
Computable2Guarded's π_par and Guarded2Network's inbox/rx naming.
Its own tiny effect class (like MonadModuleCache, not folded into FlagsEnv) since it's a
mutable-store effect, not a Reader.
- fresh : m β
Instances
A fresh identifier, guaranteed distinct from any name a user could have written: $ cannot
appear in a TLAβΊ identifier (the lexer's identifierOrKeyword only ever accepts letters,
digits, and _), so no scope-tracking is needed to avoid collisions β a syntactic argument,
not a runtime check.
Equations
Instances For
The lifts below carry MonadFresh through each transformer a pass stacks on top of the monad
that actually owns the counter β one per layer that shows up in a pass's concrete runner
(ReaderT for @'s context and Ξ, StateT for the checker's metavariable/pending-bounds
contexts, DiagT for every pass's diagnostics). Written on MonadFresh itself rather than
obtained by lifting an underlying MonadStateOf Nat: a pass says what it needs (MonadFresh),
not how the counter is stored, and the owner is free to keep it as a field of a larger state
record β which Driver/Modules.lean's DriverState does.
Lift through ReaderT β lets a pass told only [MonadFresh m] add a local ReaderT layer and
still call something needing MonadFresh under it (Desugarer/PlusCal.lean's desugarMailboxArg
wraps Expression.desugar's @-reader this way).
Equations
- instMonadFreshReaderT = { fresh := liftM MonadFresh.fresh }
Lift through StateT β Elaborator.lean's runChecker runs two of them (the metavariable
context and the pending bounds) between the checker and its base monad.
Equations
- instMonadFreshStateTOfMonad = { fresh := liftM MonadFresh.fresh }
Lift through DiagT β every pass reports through one, so this is the layer that stands
between essentially any pass and whatever owns its counter.
Equations
- instMonadFreshDiagTOfMonad = { fresh := liftM MonadFresh.fresh }
Lift through ExceptT β needed the moment a pass's own runner is a bare ExceptT rather than
going through DiagT/MonadDiagnostic (Guarded2Network's G2NM, VerifiedCompiler/, is the
first).
Equations
- instMonadFreshExceptTOfMonad = { fresh := liftM MonadFresh.fresh }
The base instance for a pass whose own runner owns a bare Nat counter directly, rather than
threading it through Driver/Modules.lean's DriverState (that file's own instance, keyed on
MonadStateOf DriverState m). Guarded2Network's G2NM := ExceptT G2NError (StateT Nat Id) is
the first stack that wants this β every earlier pass runs under the driver's counter instead.
The counter itself lives in Driver/Modules.lean's DriverState, one per compile, and every
pass β the checker, the desugarer, Computable2Guarded, Guarded2Network β draws from that
same counter for the whole compile, so compiler-introduced names cannot collide across passes.
Deliberately not a global IO.Ref: a process-wide counter makes a compile's generated names
depend on how many compiles ran before it in the same process, which is invisible in the CLI
(one compile per process) and actively wrong for the regression runner, which compiles many
fixtures in one process and checks its output for determinism.