Names and Scope
This page defines the target surface model for how names exist in Shape.
The governing rule is lexical scope:
- all user-defined names are lexically scoped
- the outermost lexical scope is the module
- Shape does not have a user-defined global namespace
The Scope Split
Section titled “The Scope Split”The target model has six categories.
1. Module Scope
Section titled “1. Module Scope”Top-level declarations and imports live in module scope.
That includes:
- user-defined
fn,type,enum,trait,interface,annotation, andmod - imported names from
from ... use { ... } - imported module namespaces from
use some_module - public exports from ordinary modules
- builtin surface API declared by stdlib modules such as
Option,Result,DateTime,print, andformat
Builtin surface names are not a separate “magic global” category in the design.
They are still owned by modules, typically under std::core::*.
from std::core::intrinsics use { Option, Result, DateTime }from std::core::remote use { @remote }use std::core::state as state2. Local Scope
Section titled “2. Local Scope”Names introduced inside a function, block, lambda, or pattern are local lexical bindings:
- parameters
let/constbindings- pattern bindings from
match - lambda parameters and captures
fn render(user) { let label = user.name match user.role { "admin" => label other => other }}Users can create local names freely, but they cannot create new names outside lexical scope.
3. Type and Associated Scope
Section titled “3. Type and Associated Scope”Some names belong to a type rather than to module scope.
That includes:
- enum variants such as
Status::Ready - associated constructors such as
Result::Ok,Result::Err,Option::Some, andOption::None - associated items attached to a type
- methods resolved from a receiver or type
from std::core::intrinsics use { Result }
let value = Result::Ok(1)The intended rule is that associated namespaces come with the type. If a type is in scope, its associated namespace is addressable through that type.
4. Syntax-Reserved Names
Section titled “4. Syntax-Reserved Names”Some spellings belong to the language grammar itself, not to modules:
- keywords such as
if,match,fn,let,return - literal spellings such as
true,false, numbers, and strings - primitive type spellings such as
int,number,string, andbool
These are part of Shape syntax. They are not imported and they are not exports.
5. The Implicit Prelude
Section titled “5. The Implicit Prelude”Shape should keep the implicit prelude intentionally tiny.
The intended rule is:
- a very small number of module-owned surface names may be available everywhere without an explicit import
printis the canonical example- everything else should be imported explicitly or accessed through a module namespace
print("hello")The prelude does not create a separate owner for a name. It only means the name was imported implicitly.
6. Internal Intrinsics
Section titled “6. Internal Intrinsics”There is also a lower-level implementation layer used by the stdlib and the compiler:
__intrinsic_*__native_*__json_*
These are not part of the public language surface. They are implementation hooks, not user-facing API. User programs should not consume them directly.
Practical Rule
Section titled “Practical Rule”If a bare name is not:
- in local scope
- in module scope through an explicit import
- available through a tiny implicit prelude import
- or syntax-reserved
then it should not resolve.
If a name belongs to a type, it should be reached through that type’s associated namespace rather than through a freestanding global binding.
Mental Model
Section titled “Mental Model”The clean mental model is:
- all user-defined names are lexically scoped
- top-level names live in modules
- builtin surface API is also module-owned
- associated constructors and variants belong to type scope
- only syntax-reserved names, the tiny implicit prelude, and internal intrinsics sit outside ordinary module/local scope
This keeps the surface explicit, avoids accidental collisions, and makes imports carry the meaning of where a capability comes from.