Table of Contents

Imports and modules

Why modules?

In Part 1, every program was a single file. That works fine for small programs, but imagine a program with 50 functions — finding anything becomes a nightmare. Modules let you split code into separate files, each focused on one topic.

Think of modules like chapters in a book. Each chapter covers one subject, and you can flip to the chapter you need. In Nyx, each .nx file can be a module.

Your first module

Let's create a math helper module. Create a file called math_helpers.nx:

// math_helpers.nx

export fn square(x: int) -> int {
    return x * x
}

export fn cube(x: int) -> int {
    return x * x * x
}

export fn factorial(n: int) -> int {
    if n <= 1 { return 1 }
    return n * factorial(n - 1)
}

The export keyword makes a function available to other files. Without export, the function is private — only usable inside its own file.

Now create your main program that uses it:

// main.nx

import { square, cube, factorial } from "math_helpers"

fn main() {
    print(square(5))       // 25
    print(cube(3))         // 27
    print(factorial(6))    // 720
}

The import statement brings specific functions from another module into your file.

The import syntax

import { name1, name2, name3 } from "module_name"

Exporting structs

You can export structs too:

// shapes.nx

export struct Circle {
    radius: int
}

export struct Rectangle {
    width: int,
    height: int
}

export fn circle_area(c: Circle) -> int {
    return 3 * c.radius * c.radius
}

export fn rect_area(r: Rectangle) -> int {
    return r.width * r.height
}
// main.nx

import { Circle, Rectangle, circle_area, rect_area } from "shapes"

fn main() {
    let c: Circle = Circle { radius: 10 }
    let r: Rectangle = Rectangle { width: 5, height: 8 }

    print(circle_area(c))    // 300
    print(rect_area(r))      // 40
}

Where Nyx looks for modules

When you write import "math_helpers", Nyx searches in this order:

  1. Project directorymath_helpers.nx relative to your project root
  2. Installed packagespackages/pkg-name/rest.nx for dependencies added via nyx add
  3. Local directorymath_helpers.nx next to your file
  4. Standard librarystd/math_helpers.nx in the Nyx installation

This means you can import from the standard library, your own files, and installed packages the same way.

Using the standard library

Nyx comes with a standard library of useful modules. Here are some examples:

import { read_file, write_file } from "std/file"
import { http_serve, http_response } from "std/http"
import { json_parse, json_stringify } from "std/json"

The std/ prefix tells Nyx to look in the standard library directory.

Organizing a project

A typical Nyx project might look like this:

my_project/
├── main.nx           # Entry point
├── models.nx         # Data structures
├── database.nx       # Database operations
└── utils.nx          # Helper functions
// models.nx
export struct User {
    name: String,
    email: String,
    age: int
}

export struct Post {
    title: String,
    body: String,
    author: String
}
// utils.nx
export fn repeat_string(s: String, times: int) -> String {
    var result: String = ""
    var i: int = 0
    while i < times {
        result = result + s
        i += 1
    }
    return result
}

export fn clamp(value: int, low: int, high: int) -> int {
    if value < low { return low }
    if value > high { return high }
    return value
}
// main.nx
import { User, Post } from "models"
import { repeat_string, clamp } from "utils"

fn main() {
    let u: User = User { name: "Alice", email: "alice@example.com", age: 30 }
    print(u.name)

    print(repeat_string("=-", 20))    // =-=-=-=-=-=-...
    print(clamp(150, 0, 100))         // 100
}

What export means

Only exported items are visible outside the module. This is important for hiding implementation details:

// password.nx

// Private — only usable inside this file
fn hash_internal(s: String, rounds: int) -> String {
    var result: String = s
    var i: int = 0
    while i < rounds {
        result = result + "*"
        i += 1
    }
    return result
}

// Public — other files can use this
export fn hash_password(password: String) -> String {
    return hash_internal(password, 10)
}

If another file tries to import hash_internal, the compiler will give an error. This protects your internal logic.

Practical example: a mini library

Let's build a small string utility library:

// string_utils.nx

export fn is_empty(s: String) -> bool {
    return s.length() == 0
}

export fn starts_with_upper(s: String) -> bool {
    if s.length() == 0 { return false }
    let c: int = s.charAt(0)
    return c >= 65 and c <= 90
}

export fn count_words(s: String) -> int {
    if s.length() == 0 { return 0 }
    let parts: Array = s.split(" ")
    return parts.length()
}

export fn truncate(s: String, max_len: int) -> String {
    if s.length() <= max_len { return s }
    return s.substring(0, max_len) + "..."
}
// main.nx

import { is_empty, starts_with_upper, count_words, truncate } from "string_utils"

fn main() {
    print(is_empty(""))                    // true
    print(is_empty("hello"))               // false
    print(starts_with_upper("Hello"))      // true
    print(starts_with_upper("hello"))      // false
    print(count_words("Nyx is a compiled language"))    // 5
    print(truncate("Hello, World!", 5))    // Hello...
}

Exercises

  1. Create a module math_utils.nx that exports functions abs(n: int) -> int, max(a: int, b: int) -> int, and min(a: int, b: int) -> int. Write a main program that imports and uses them.
  1. Create a module validators.nx that exports is_positive(n: int) -> bool, is_even(n: int) -> bool, and in_range(n: int, low: int, high: int) -> bool. Use them in a main program.
  1. Split the student grade tracker from Chapter 10 into modules: student.nx (struct + creation), grades.nx (average, highest, letter grade), and main.nx (report and entry point).
  1. Create a module array_utils.nx that exports sum(arr: Array) -> int, average(arr: Array) -> int, and contains(arr: Array, target: int) -> bool. Test it from a main file.
  1. Create two modules that both export a function with the same logic but different names. Import both into a main file and verify they work together without conflicts.

Summary

Next chapter: Files →

← Previous: Your first project Next: Files →