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"
- The names inside
{ }are the functions (or structs) you want to use. - The string after
fromis the module name — the filename without.nx. - You only import what you need. This keeps your code clean and makes dependencies clear.
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:
- Project directory —
math_helpers.nxrelative to your project root - Installed packages —
packages/pkg-name/rest.nxfor dependencies added vianyx add - Local directory —
math_helpers.nxnext to your file - Standard library —
std/math_helpers.nxin 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
- Create a module
math_utils.nxthat exports functionsabs(n: int) -> int,max(a: int, b: int) -> int, andmin(a: int, b: int) -> int. Write a main program that imports and uses them.
- Create a module
validators.nxthat exportsis_positive(n: int) -> bool,is_even(n: int) -> bool, andin_range(n: int, low: int, high: int) -> bool. Use them in a main program.
- Split the student grade tracker from Chapter 10 into modules:
student.nx(struct + creation),grades.nx(average, highest, letter grade), andmain.nx(report and entry point).
- Create a module
array_utils.nxthat exportssum(arr: Array) -> int,average(arr: Array) -> int, andcontains(arr: Array, target: int) -> bool. Test it from a main file.
- 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
- Use
exportto make functions and structs available to other files. - Use
import { names } from "module"to bring items into your file. - Nyx searches: local directory → standard library → packages.
- Modules help you organize code into focused, reusable files.
- Only export what other files need — keep implementation details private.
- The standard library is imported the same way:
from "std/module".
Next chapter: Files →