Imports & Modules
Import syntax
import io from "io";
import math from "math";
let root: f64 = math.sqrt_f64(2.0);
io.println("{math.gcd(12, 18)}");All imports use the form import local_name from "module_path";. The local name is used as a namespace prefix when accessing the module’s exports.
Accessing exports
Imported items are accessed with dot notation:
import str from "str";
import ui from "ui";
let shout: string = str.to_upper(name);
let kind = ui.PointerKind.Down;Functions, structs, enums, tagged unions, and components from the imported module are available as module.Name.
A pub var of another module is a variable like your own: index it, take a field of an element, call a method on it, take its address, and assign to any of these:
import other from "other"; // pub var table: [4]Rec;
other.table[2].count = 5;
other.table[i].count += 1;
let n = other.table[2].count + other.table.len;
let p: *other.Rec = &other.table[2];A pub const has the value it has in its module, however that module computed it: pub const B: u16 = a.A; in c is c.B (4) to a module that imports c and not a. It is used like that value: geo.ORIGIN.x, keys.TABLE[i].code, keys.TABLE.len, and an integer one sizes an array: var pressed: [keys.COUNT]bool;.
A pub function is also a value under its module’s name, wherever a function pointer goes: list.sort_by(items, cmp.by_name), let log: fn([]const u8) = io.println;. It is the same function, at the same address, as the defining module’s own by_name. A function that isn’t pub can’t be named from another module: 'helper' is not pub in module 'cmp'.
Selective imports
Import specific symbols directly into your namespace without a module prefix:
import { println, eprintln } from "io";
import { Vec3, make_vec } from "./veclib";
println("distance: {make_vec(1, 2, 3).x}");
eprintln("done");Selective imports bring individual pub symbols into scope as if they were declared locally. No prefix needed.
You can use both forms together:
import io from "io";
import { println } from "io";
println("short form"); // selective — no prefix
io.eprintln("oops"); // qualified — still worksRules:
- Only
pubsymbols can be selectively imported - Name collisions (with local declarations or other selective imports) are a compile error:
`count` is already defined at app.tape:2 - Importing a
varbinds to the module’s actual storage — mutations are visible to both modules, same asmodule.varnamequalified access
Not supported — glob imports:
import * from "io"; // COMPILE ERROR — not valid syntaxGlob imports will never be supported in tape. The reasons are fundamental to the language’s philosophy:
-
Readability over convenience. When you encounter
println()in code, you should be able to look at the imports and immediately know where it comes from. Glob imports force the reader to check every imported module to find the source of a symbol. -
No silent breakage. If module A adds a new
pub fn process()and your file already has a localfn process(), a glob import from A would silently collide. With explicit lists, the module author can add exports freely without breaking downstream code. -
Predictable over clever. Tape code should mean the same thing regardless of what’s happening in other files. A glob import couples your file’s name resolution to the full public surface of another module — any addition there can change what names mean here.
Always list symbols explicitly. The small upfront cost of typing names pays back every time someone reads the code.
Module identity
Each .tape file is a module. The import path determines which file to load:
- Bare names (
"io","mem","str") — resolved from the include search paths (stdlib, project lib dirs) - Relative paths (
"./utils","../shared/types") — resolved relative to the importing file
The .tape extension is appended automatically if not present.
Type identity
A struct, enum or tagged union is one type however you reach it. If shapes uses geo.Point in its functions, and your module imports both, shapes’ points are your geo.Points, under whatever alias you import geo (or by a selective import { Point }), and containers shapes generates of them hold the same type:
import shapes from "./shapes.tape"; // uses geo.Point in its API
import g from "./geo.tape";
let p: g.Point = g.point(1, 2);
shapes.move(p); // fine: one declaration, one typeTwo declarations are two types, even with the same name and fields (or the same variants).
Visibility
By default, declarations are module-private. Use pub to export:
pub fn api_function() -> i64 { return 42; }
fn internal_helper() -> i64 { return api_function() * 2; }
pub struct Config {
name: string;
value: i64;
}
pub enum Direction { North; South; East; West; }
pub component Button {
prop label: string = "";
event on_click();
}What can be pub:
| Declaration | pub supported |
|---|---|
| Functions | Yes |
| Structs | Yes |
| Enums | Yes |
| Tagged unions | Yes |
| Components | Yes |
| Constants | Yes |
| Variables (var/let) | Yes |
| Type aliases | Yes (pub type; the right-hand side may only name types an importer can see) |
| Struct fields | No (all fields visible if struct is pub) |
pub {} grouping block
Group multiple declarations under a single pub to export them all:
pub {
fn increment() { count = count + 1; }
fn decrement() { count = count - 1; }
fn reset() { count = 0; }
var count: i64 = 0;
}
// Still private:
fn internal_helper() { }This is pure syntactic sugar — equivalent to writing pub on each declaration individually. Useful when extracting a batch of related functions into a new module.
Rules:
- Only valid at module top level in
#coderegion - Any declaration that supports
pubcan appear inside the block - Cannot contain non-declaration statements (no expressions, no control flow)
- Nesting
pub {}insidepub {}is redundant but not an error
One name, one declaration
An import’s name belongs to the module like its own declarations: an alias
(import util from "util";) or a selectively imported symbol that is also the
name of a function, constant, variable or type of the module, or of another
import, is a compile error. Every name of a module means one thing:
import draw from "draw";
pub fn draw() { ... } // error: `draw` is already defined at ...:1, as an importRename the alias (import drawing from "draw";) or the declaration.
How cross-module linking works
When module A imports module B, the compiler:
- Parses B and collects all
pubdeclarations - Injects stubs into A for them: functions with qualified names (
module.fn_name), structs, enums, components and variables, each recording the declaration it stands for - Type-checks A’s uses against those stubs
- When the program is put together, each use goes to the very declaration its stub stands for: never to another one found by name (another import’s
one, another methodget)
Standard library modules
import io from "io"; // console, streams, buffering, IoError
import str from "str"; // building strings: join, replace, pad, Builder
import cstr from "cstr"; // NUL-terminated C strings as []const u8
import fmt from "fmt"; // hex, binary, fixed-point and size formatting
import path from "path"; // joining and splitting file paths
import mem from "mem"; // allocation, copy, compare
import math from "math"; // math functions
import fs from "fs"; // files and directories
import os from "os"; // arguments, environment, platform
import time from "time"; // clocks, sleep, dates
import gfx from "gfx"; // graphics primitives
import ui from "ui"; // UI framework, layout, events
import widgets from "widgets"; // standard widget components
import colors from "color"; // color utilitiesMultiple imports
Each import is a separate statement:
import io from "io";
import str from "str";
import mem from "mem";Relative imports
Import from files relative to the current module:
import utils from "./utils";
import types from "../shared/types";Region-specific imports
A #style region imports another module’s #style region with a path
ending in #style:
#style
import theme from "theme#style";It gets that region’s pub consts (theme.accent) and its rules (see
#style). This import is written only in #style;
a #code import names the module itself and doesn’t see its #style.
"app#view") can’t be imported on their own yet.Last modified: