Skip to content
CryoCryo home
LanguageProgram structure

14Modules and Imports

Cryo organises code into modules using a hierarchical namespace system. Every source file declares its namespace, and files reference each other through import.

14.1 Namespaces

Every Cryo source file begins with a namespace declaration that establishes its position in the module hierarchy.

namespace MyApp;
namespace MyApp::Utils;
namespace std::collections::array;

The namespace serves as the file's identity within the project. The compiler uses it to resolve imports and to mangle symbol names so that two unrelated functions named parse from different modules do not collide at link time.

14.2 Module Aggregators

A directory of related files uses a _module.cryo aggregator to declare which submodules exist and which are public, analogous to Rust's mod.rs.

// stdlib/collections/_module.cryo
namespace std::collections;

public module collections::raw_buffer;
public module collections::array;
public module collections::str;
public module collections::string;
public module collections::hashmap;
public module collections::hashset;

When code imports std::collections, only the modules declared public in the aggregator are visible.

14.3 Imports

import Math::Vector;                  // import the module
import Math::Vector::*;               // wildcard: everything public
import Math::Vector as V;             // aliased
import Math::Vector::{ Vec2, Vec3 };  // selective import (brace list)

Each import declaration imports from a single path. To bring two items from the same module into scope, use the selective brace form (import M::{A, B};) or write two separate import statements.

Wildcard imports are convenient but can cause name collisions; prefer the brace form or using the module name directly.

14.4 Visibility

ModifierMeaning
(none) / publicAccessible to any module that imports this one. This is the default for top-level items.
privateAccessible only within the same module.
protectedClass-only; accessible to the class and its subclasses.

Top-level items are public by default; mark an item private to confine it to its own module. For top-level types (struct / class / enum), private is enforced across modules: naming a private type from another module - in a type annotation, a struct literal, or a function signature - is rejected with E0503. A private type remains fully usable within its own module.

This is the mechanism behind hiding iterator engines: a cursor struct can be private while its producer returns implement Iterator<...>, so callers consume the sequence through the trait and can never name the concrete type.

// engine.cryo
namespace app::engine;
private type struct Cursor { /* ... */ }          // hidden outside app::engine
public function scan(...) -> implement Iterator<i32> { return Cursor { ... }; }

// main.cryo
namespace app;
import app::engine;
for (x in scan(...)) { ... }                     // fine - never names Cursor
mut c: Cursor = ...;                             // E0503: `Cursor` is private to `app::engine`