Albite

Modules and visibility

Declaring submodules

mod declares a submodule, which the compiler expects to find as either name.tg or name/mod.tg:

mod lexer;
mod parser;
mod elab;

A typical layout:

module_example/
  mod.tg          entry point
  greet.tg
  math/
    mod.tg        declares its own sub-modules
    ops.tg
// module_example/mod.tg
pub mod math;
pub mod greet;

use greet::answer;

fn main() -> Nat {
    answer()
}
// module_example/math/ops.tg
pub fn add(a: Nat, b: Nat) -> Nat {
    a + b
}

Importing

use lexer::token::Token;
use driver::{parse_args, dispatch_command, process_exit};

Paths are absolute from the crate root. There is no super and no relative import — a path means the same thing wherever it is written, which is worth the occasional extra segment.

Visibility

Declarations are private to their module unless marked pub:

pub fn public_function() -> Nat { 1 }
pub type PublicType = { value: Nat }
pub extern "C" fn public_extern() -> Unit

pub applies to modules too — pub mod math; makes the module itself reachable. Visibility is enforced per constructor and per field, so a pub type does not automatically expose everything inside it.

Import aliasing

Since 1.5

Rename on import to resolve a collision or shorten a name:

use util::original_name as renamed;
use lexer::token::Token as LexToken;

Modules are properly namespaced

Two modules may each define label without interfering: v1.5 removed the flattening pass that used to merge everything into one namespace after elaboration, and each module is now elaborated on its own.

A known divergence. With same-named definitions in sibling modules, the interpreter currently resolves the wrong one — it binds the last-declared module’s version regardless of what the use names. Compiled binaries are correct. Until this is fixed, avoid exporting the same name from two modules in the same program, or verify with albite compile rather than albite run. See Current limitations.