Imports guide

Split your app into multiple .ny files and share functions, types, and constants with import "path.ny" or selective import { name } from "path.ny" — then build from the project root.

How imports work

When you run nyra run . or nyra build ., the compiler starts at main.ny and recursively loads every import it finds. Definitions are merged into one program (like compiling one big file). Whole-module imports bring all pub symbols; selective imports bring only the names you list (plus same-file helpers those names need).

main.ny
  import "src/util.ny"              ──►  all pub from util.ny
  import { add } from "math.ny"     ──►  only add (+ its helpers)
  import "models/user.ny" as u      ──►  u::… / u__…
         │
         ▼
  Merged program → typecheck → borrow → LLVM
Always use project mode for multi-file apps nyra run main.ny compiles only that file and does not follow imports. Use nyra run . from the directory that contains main.ny.

Path rules

Import stringResolved from
"src/foo.ny"Relative to the file containing the import
"types.ny"Same directory as the importing file
"stdlib/vec.ny"$NYRA_HOME/share/stdlib/, install tree, or repo stdlib/ — works from any project depth
"std/json/mod"Alias for stdlib/json/mod.ny (extension optional)
"stdlib/prelude.ny"Bundle: vec, map, strings/ops, log
"pkg/ny-sqlite".nyra/cache/ny-sqlite/ after nyrapkg install ny-sqlite@^0.1.0 (see nyrapkg)

Import forms

SyntaxWhat mergesUse when
import "math.ny" All pub symbols from the file Small modules / you need most of the API
import "math.ny" as m All pub symbols as m::name / m__name Avoid name clashes; keep a module prefix
import { add, mul } from "math.ny" Only those pub names (+ same-file helpers they call) Lean builds — prefer for large files
import { greet as hi } from "helpers.ny" One pub symbol, renamed locally Clearer call-site names

Visibility: omit modifier or use pub to export; priv stays internal. Selective imports of a priv name fail with E039.

Selective imports

Prefer import { … } from "…" when you only need a few symbols. Even a single name still uses braces: import { add } from "math.ny".

myapp/ main.ny src/ math.ny
src/math.ny
pub fn add(a, b) {
    return a + b
}

pub fn mul(a, b) {
    return a * b
}

pub fn unused_export() {
    return 0
}
main.ny
import { add, mul } from "src/math.ny"

fn main() {
    print(add(10, 20))   // 30
    print(mul(3, 7))     // 21
    // unused_export is not merged
}

Rename with as

import { greet as hi } from "src/helpers.ny"

fn main() {
    print(hi("nyra"))
}
Same-file helpers If a selected function calls another function in the same file (even priv), the compiler pulls that dependency into the merge automatically. Unrelated pub exports stay out.

Importing functions

Define functions in a module file; call them from main.ny after importing (whole module or selective).

myapp/ main.ny src/ math.ny
src/math.ny
fn double(x){
    return x * 2
}

fn triple(x) {
    return x * 3
}
main.ny
import "src/math.ny"

fn main() {
    print(double(5))   // 10
    print(triple(5))   // 15
}
cd myapp
nyra run .

There is no per-item export keyword — top-level items default to pub. Mark helpers priv so whole-module imports skip them. Prefer selective import { … } when you only need a few names (see selective imports).

Importing structs

Structs live in one file; other files import that file to use the type and its fields.

models/point.ny
struct Point {
    x: i32
    y: i32
}

fn origin() {
    return Point { x: 0, y: 0 }
}
main.ny
import "models/point.ny"

fn main() {
    let p = origin()
    print(p.x)
    print(p.y)
}

Split types and operations (calculator pattern)

Real project in the Nyra repo: examples/projects/calculator/

types.nystruct Calculator {
    value: i32
}
ops.ny
import "types.ny"

fn add_calc(c, n) {
    return Calculator { value: c.value + n }
}
main.ny
import "types.ny"
import "ops.ny"

fn main() {
    let c = Calculator { value: 0 }
    let c2 = add_calc(c, 10)
    print(c2.value)
}

Importing enums

Enum definitions merge like functions. Use Type.Variant syntax after import.

colors.ny
enum Status {
    Ok
    Err
}

fn is_ok(s) {
    return match s {
        Status.Ok => true
        Status.Err => false
    }
}
main.ny
import "colors.ny"

fn main() {
    let s = Status.Ok
    print(is_ok(s))
}

Importing constants

Top-level const bindings in imported files are merged into the program.

config.nyconst MAX_USERS = 100
const APP_NAME = "MyApp"
main.ny
import "config.ny"

fn main() {
    print(MAX_USERS)
    print(APP_NAME)
}

Standard library files

Runtime helpers are declared in stdlib/*.ny and backed by the Nyra C runtime. Builtins print, time_start/time_end, mem_start/mem_end need no import.

main.ny
import "stdlib/fs.ny"

fn main() {
    let s = read_file("data.txt")
    print(s)
    // compiler auto-drops s — no free needed
}

Use short paths — no ../../../stdlib/... from examples:

import "stdlib/prelude.ny"
import "stdlib/async.ny"
import "stdlib/json/mod.ny"

Full function list: Standard library. Refresh installed stdlib with ./scripts/updateLang.sh after upgrades.

Rules & pitfalls

TopicGuidance
Duplicate namesTwo imported files must not define the same fn / struct name; merge keeps the first. Use as alias or selective rename
fn mainOnly one entry — keep it in main.ny
Import cyclesAvoid a.ny imports b.ny and b.ny imports a.ny
VisibilityDefault pub; priv is hidden from importers. Selective import of privE039
Missing selective nameimport { missing } from "…"E039 (lists available pub symbols)
Single-name selectiveStill needs braces: import { add } from "math.ny"
Variableslet inside functions is local — not “exported”. Share data via const, functions, or structs
Mutable globalsNot supported as imports; use functions that return state
Run commandnyra run . not nyra run main.ny for multi-file
Unused importW002 — remove the line or run nyra pkg prune
Works
nyra run .
nyra build .
nyra check .
Single-file only
nyra run main.ny
# imports ignored

More runnable snippets: Built-in methods.