Many things are given names: variables, functions, types, fields, methods, etc. The collection of all names defined for a scope is called a namespace. Blocks and types are two different kinds of namespaces.

Namespaces facilitate a modular architecture by:

The rules governing names vary according to the type of namespace it belongs to. Let's review those here, beginning with the introduction of a new kind of namespace: the module.

Modules

A module is a global namespace. You have seen code examples for modules already:

// A type declaration
struct Handler:
  id u32

// a global variable
imm handler = Handler[0]

// A function
fn main():
  // do some stuff

As this example shows, modules hold global variables, functions, and type declarations. They can also hold other named things, such as macros and even other named modules. Further, modules can import names from other modules.

Every program or library has a single main module. That main module may import other modules or types offering reusable, isolated capabilities it depends on.

Name rules

Module names are:

Global variables

Global variables across all modules (and types) are managed by the global region. Memory space for them is allocated automatically and all-at-once when a program is loaded for execution. The lifetime of all global variables is the same as the program's lifetime, since global variables come into being when the program starts and only disappear once the program stops. The lifetime annotation for this scope is 'static.

Global variable declarations have two important constraints:

Dynamic initialization of global variables

Sometimes, the initial value of a global variable is unknown, or too complex to specify, at compile-time. In such cases, we want to initialize the variable using run-time logic invoked when the program or library is loaded. To accomplish this, declare the variable without an initial value and then initialize that variable inside the module's initializer, a function called init.

imm handler

fn @initpure init():
  handler = Handler()

The initializer is declared with initpure, a less constrained version of pure. Any function marked as initpure may not read any uninitialized global variables in the current module and may only call functions marked as pure or initpure.

To ensure safe and complete dynamic initialization, the compiler:

A finalizer might be automatically be created, where needed, to finalize any dynamically-initialized global variables. This is run when the program is shutting down.

Types as Namespaces

Declarations for struct and enum types are also namespaces. At a minimum, these hold the names of the type's fields and methods. They may also hold the names for the type's static functions and the names of other types (e.g., enums).

Static Functions

In addition to methods, a type may declare useful static functions that are not methods and do not work with a specific object of that type. All such functions should be declared within a static block.

struct Counter:
  static:
    fn zeroFactory() Counter:
      Counter[0]

To make use of the function outside the type, qualify the name with the name of the type:

mut cnt = Counter::factory()

Name Rules

A type's names are:

Function Blocks

All logic blocks inside functions and methods are namespaces that hold local (and parameter) variables.

Every block's names are:

_