Metaprogramming · July 29, 2026
Somewhere, Sometime
A macro environment is not a bag of helpers. It is a coordinate in the program: a place, a phase, and a deliberately local here.
Environment is a coordinate
“Environment” can sound like a miscellaneous object passed to a callback: a place to collect services, flags, and helper methods. That is useful, but it is not the idea here.
In Range, a macro environment answers three questions at once. Where in the program are we? When is this code running? What does “here” have the authority to observe and change?
macro project(): Construct {
#environment {
construct ProjectDefaults {
// declarations projected into this environment
}
}
}Somewhere
A macro is attached somewhere: to a project, a construct, a member, or an application. Its environment carries that location. Queries such as the target, source, ancestors, system defaults, and graph relationships are not global reflection. They are views from a particular coordinate.
Sometime
Macro code runs at a different time from the code it produces. It can inspect compile-time values, make decisions, and project syntax that will participate in later compilation and execution.
The # makes that crossing visible. A value known now becomes
part of code that exists next.
#environment {
construct ProjectDefaults {}
}#environment {
construct ProjectDefaults {
#environment.system.defaults.map { default in
let #default.identifier: #default.value
}
}
}let collection: [Let](
#environment.target.Declaration.members.filter(all: Let)
)
#environment {
extension #environment.target.Declaration.identifier {
#collection.map { item in
let #item.identifier: #item.value
}
}
}Place
“Here” should be smaller than “everywhere.” The environment gives a macro local authority and makes its observations explicit. That lets the graph remember what the macro depended on, reject capabilities it does not have, and reconsider the expansion when its particular world changes.
Somewhere gives the macro a place. Sometime gives it a phase. Some place gives it a boundary.
Not expand. Environment.
That is why #environment { ... } feels closer to the idea
than asking an object to expand. The block is not merely a
method call that returns syntax. It names the contextual surface through
which declarations enter the program.
The spelling is small, but the model is large: metaprogramming grounded in where, when, and here.