Modules & Namespaces

Modules and Manifests

Sun uses a manifest-based module system where programs declare their dependencies in a manifest block. Multiple source files are merged into a single AST before compilation, enabling efficient cross-file optimization.

Manifests

Every Sun program has an entrypoint file containing a manifest block that declares all dependencies:

main.sun
using sun;
 
function main() void {
    println("Hello!");
}
 
manifest {
    suns: ["utils.sun", "math.sun"]   // Source files to compile together
    moons: ["stdlib.moon"]             // Precompiled libraries to link
}

Manifest Syntax

manifest {
    suns: ["file1.sun", "file2.sun"]   // Optional: additional source files
    moons: ["lib1.moon", "lib2.moon"]  // Optional: precompiled libraries
    protos: ["schemas/msgs.proto"]     // Optional: protobuf schemas to import
}
  • suns — Source files (.sun) to parse and merge with the entrypoint
  • moons — Precompiled library bundles (.moon) to link
  • protos — Protobuf schemas (.proto) compiled into message classes; see Protobuf

All fields are optional (entries are newline-separated; a trailing ; is tolerated). A minimal program needs only a main() function.

Path Resolution

Paths in the manifest are resolved in order:

  1. Relative to the entrypoint file — First, check if the path exists relative to the directory containing the entrypoint
  2. Via SUN_PATH — If not found, search in the SUN_PATH environment variable
export SUN_PATH=/path/to/sun/build
sun myprogram.sun   # Can find stdlib.moon in SUN_PATH

Compilation Pipeline

When you run sun main.sun:

  1. Parse entrypoint — Parse main.sun and extract the manifest
  2. Parse dependencies — Parse all files listed in suns; synthesize a module of message classes for each schema in protos
  3. Merge ASTs — Combine all parsed ASTs into a single merged AST
  4. Load moons — Load type stubs and compiled code from moons
  5. Semantic analysis — Analyze the merged AST with moon type information
  6. Code generation — Generate LLVM IR for the merged AST
  7. Link — Link with moon compiled code
  8. Execute or output — JIT execute or emit native binary

Precompiled Libraries (.moon)

Moon files (.moon) are precompiled library bundles containing compiled code and type information. They enable fast compilation and reliable distribution.

Sun is designed to work with Moon, a safety-critical package manager (coming soon) that pins dependencies to the exact hash of the binary and compiler version. Every change is a breaking change — Moon prioritizes reproducibility over convenience.

Using Moon Files

Declare moon dependencies in your manifest:

using sun;
 
function main() i32 {
    var allocator = make_heap_allocator();
    var m = Matrix<i32>(allocator, [3, 3]);
    m[0, 0] = 42;
    return m[0, 0];
}
 
manifest {
    moons: ["stdlib.moon"]
}

Creating Moon Files

Create a moon from an entrypoint file with a manifest:

mylib.sun (entrypoint)

public module mylib {
    public function helper() i32 { return 42; }
}
 
manifest {
    suns: ["vec2.sun", "utils.sun"]
}
sun --emit-moon -o mylib.moon mylib.sun

The compiler:

  1. Parses the entrypoint and all files listed in suns
  2. Merges them into a single AST
  3. Compiles and serializes the code and type information
  4. Packages everything into mylib.moon

The Standard Library

Sun's standard library is built from stdlib/stdlib.sun:

stdlib/stdlib.sun
manifest {
    suns: [
        "allocator.sun", "unique.sun", "errors.sun", "sys.sun", "option.sun",
        "math.sun", "iterator.sun", "slice.sun", "contiguous_buffer.sun",
        "vec.sun", "matrix.sun", "string.sun", "print.sun", "map.sun",
        "linked_list.sun", "mutex.sun", "io.sun", "env.sun", "time.sun",
        "process.sun", "networking.sun", "http.sun", "proto_wire.sun",
        "json.sun", "bigint.sun"
    ]
}
ModuleContents
allocator.sunMemory allocators (HeapAllocator, IAllocator interface)
bigint.sunArbitrary-precision BigUint
env.sunsun.env — environment variables, working directory, args
errors.sunStandard error types (EmptyError, NotFoundError, etc.)
http.sunHTTP client and server
io.sunsun.io — files, directories, Poller
iterator.sunIteration protocol (IIterator<T, Container>, IIterable<T, Self>)
json.sunJson parsing and serialization
linked_list.sunDoubly-linked list LinkedList<T>
map.sunGeneric hash map Map<K, V>
matrix.sunMatrix<T> and MatrixView<T> N-dimensional arrays
mutex.sunMutex for thread synchronization
networking.sunTCP sockets (TcpStream, TcpListener)
option.sunOption<T> and Result<T, E> payload enums
print.sunOverloaded print/println/eprint/eprintln
process.sunsun.processCommand, signals, raw fork/exec
proto_wire.sunProtobuf wire format
string.sunString and StringView classes
sys.sunThe stdlib's private extern "C" declarations
time.sunsun.timeDuration, Instant, DateTime
unique.sunUnique<T> smart pointer with automatic cleanup
vec.sunGeneric growable array Vec<T>

When building from source, the stdlib is automatically compiled:

cmake -B build && cmake --build build
# Creates build/stdlib.moon

Example: Building a Custom Moon

Step 1: Create library source files

mylib/vec2.sun

class Vec2 {
    var x: f64;
    var y: f64;
    
    function init(x_: f64, y_: f64) {
        this.x = x_;
        this.y = y_;
    }
    
    function add(other: ref Vec2) Vec2 {
        return Vec2(this.x + other.x, this.y + other.y);
    }
    
    function length() f64 {
        return _sqrt(this.x * this.x + this.y * this.y);
    }
}

mylib/utils.sun

function clamp(val: f64, min: f64, max: f64) f64 {
    if (val < min) { return min; }
    if (val > max) { return max; }
    return val;
}

Step 2: Create entrypoint with manifest

mylib/mylib.sun

manifest {
    suns: ["vec2.sun", "utils.sun"]
}

Step 3: Build the moon

sun --emit-moon -o mylib.moon mylib/mylib.sun

Step 4: Use the library

function main() i32 {
    var a = Vec2(3.0, 4.0);
    var len = a.length();    // 5.0
    var t = clamp(1.5, 0.0, 1.0);  // 1.0
    return 0;
}
 
manifest {
    moons: ["mylib.moon"]
}

Installing Moon Files

Per-project:

cp mylib.moon myproject/libs/
manifest {
    moons: ["libs/mylib.moon"]
}

System-wide:

mkdir -p ~/.sun/lib
cp mylib.moon ~/.sun/lib/
export SUN_PATH=~/.sun/lib
manifest {
    moons: ["mylib.moon"]   // Found via SUN_PATH
}

Namespaces (module blocks)

Use module blocks to group related types and functions:

public module math {
    public function square(x: i32) i32 {
        return x * x;
    }
    
    public function cube(x: i32) i32 {
        return x * x * x;
    }
}

Symbols inside a module are mangled with the module name prefix (e.g., math_square).

Using Statements

Use using to import all symbols from a module:

using sun;   // Import all symbols from the sun module
 
function main() i32 {
    var allocator = make_heap_allocator();  // No qualification needed
    var v = Vec<i32>(allocator, 8);
    v.push(42);
    return v.get(0);
}
 
manifest {
    moons: ["stdlib.moon"]
}

Without using, you would need to reference symbols by their mangled names.

Module Scope

Inside a module, symbols can reference each other directly:

public module math {
    public function square(x: i32) i32 {
        return x * x;
    }
 
    public function sum_of_squares(a: i32, b: i32) i32 {
        // Can call square() directly - same module
        return square(a) + square(b);
    }
}

Visibility

Everything is private by default. public is the only modifier; there is no private keyword — an item is private simply by not being marked public.

Privacy is scoped to the module: a private item is reachable from anywhere inside the module that declares it, including nested (child) modules, and from nowhere else. Files are not a boundary — every opening of module sun { ... } across the stdlib's files is one module.

public module geometry {
    // Private helper: callable anywhere inside `geometry` (and its children)
    function square(x: f64) f64 { return x * x; }
 
    public class Point {
        var x: f64;                    // private field: `geometry` only
        var y: f64;
        public function init(x: f64, y: f64) { this.x = x; this.y = y; }
        public function norm2() f64 { return square(this.x) + square(this.y); }
    }
 
    module internal {                  // private nested module
        public function scratch() i32 { return 0; }
    }
 
    public module shapes {             // public nested module
        public function unit() Point { return Point(1.0, 0.0); }
    }
}
 
using geometry;
 
function main() i32 {
    var p = shapes.unit();
    var n = p.norm2();        // OK: public method
    // p.x                    // error: 'x' is private to class 'Point' in module 'geometry'
    // square(2.0)            // error: function 'square' is private to module 'geometry'
    // internal.scratch()     // error: module 'internal' is private to module 'geometry'
    return 0;
}

The modifier applies to every kind of module-level item — functions, classes, interfaces, enums, globals, extern and declare declarations, and nested modules — and to class and interface members. public module a.b { ... } makes both a and b public. All openings of a module must agree on its visibility. using M; never exposes M's private items.

Items declared outside any module belong to the program's root scope and are reachable everywhere in that program (a single-file program needs no public at all).

Visibility and .moon bundles

A bundle keeps every item — private ones included, since generic method bodies shipped in the bundle may use them — but importers can only reach the public API. --emit-moon requires each top-level module of the bundle to be public; a bundle whose root module is private is rejected.