What a module is
A module is just another .th file. The file that starts the program is the main file; any file it imports is a module. Modules export their functions and structs; everything else stays private.
There are four import forms: import, from…import, lazyimport, and lazyfrom.
import: the whole module
import makes the module's functions and structs available under the module's name. Call them qualified, with a dot.
import math
def main() -> Int32
print(math.abs(-5))
return 0
An alias renames the module in your file:
import math as m
print(m.abs(-5))
from: pick names
from pulls specific members in by their plain name, with optional aliases. Only functions and structs can be imported.
from math import abs, clamp
from math import pow as power
Both import styles can be mixed in one file, as in the 03_imports example:
from math import abs, clamp
import math
def main() -> Int32
a: Int32 = abs(-5)
b: Int32 = math.clamp(99, 0, 50)
c: Int32 = math.abs(-9)
print(a + b + c)
return 0
Lazy imports
lazyimport and lazyfrom are the same thing, but the module isn't loaded until one of its names is actually used. The module file still needs to exist; the resolution just happens later.
lazyimport math
lazyfrom math import abs
def main() -> Int32
print(abs(-7)) # math is loaded here, on first use
return 0
Importing structs
Structs import the same way as functions, and once imported you use them like any local struct.
from geom import Point
p: Point = Point(x=3, y=4)
You can't define a struct and then import it as a function, and vice versa. The compiler checks. Importing a variable is an error:
Using C, C++, Rust, and Python modules
Threadon can call functions written in other languages. This is done through manifest files, which describe how the external code should be compiled and linked. Four backends are supported: C, C++, Rust, and Python.
The lang field in the manifest selects the backend:
Manifest files
A module written in another language uses a .manifest file to describe how Threadon should load it. The manifest tells the compiler which language the module is written in, where its source file lives, and which functions it exports to Threadon.
A manifest file is a plain text file with simple key–value pairs. Here is an example:
module PyAdd
lang python
source padd.py
export mean List Float64 Float64
export scale Float64 Float64 Float64
export normalize List Float64 List Float64
export describe Dict String Float64 Dict String Float64
export add Float64 Float64 Float64
The module name becomes the import name inside Threadon. The lang field selects the foreign-language backend. The source field points to the file that implements the module. Each export line declares a function name followed by its parameter types and return type.
The compiler uses the manifest to generate the glue code needed to call the foreign functions safely. If the manifest is missing a function, Threadon cannot import it. If the types do not match, the compiler rejects the manifest.
lang c
lang cpp
lang rust
lang python
Each backend has its own toolchain:
- C modules compile to a shared library using the system C compiler.
- C++ modules use the C++ compiler and support classes mapped to Threadon structs.
- Rust modules are built with
cargoand exposeextern "C"functions. - Python modules are wrapped automatically using the Python bridge, allowing Python functions to behave like native Threadon functions.
Threadon ensures type safety by checking the manifest against the foreign function signatures. If the types do not match, the module will not load. Once imported, foreign functions behave exactly like normal Threadon functions.
This makes it possible to write performance‑critical code in C or Rust, reuse existing C++ libraries, or call Python code directly from Threadon without rewriting it.
Rules
- No wildcard imports.
from m import *is rejected: "Wildcard imports (*) are not allowed". - No trailing commas in the import list.
- Names must exist. Importing a member that isn't in the module is an error: "Module 'm' has no member 'x'".
- No circular imports. If
aimportsbandbimportsa, you get: "Circular import detected: a -> b -> a".
How modules are found
The importer searches these places, in order:
-I DIRdirectories from the command line, if any.- The folder the main file is in.
- The
stdlib/directory that ships with Threadon.
Dotted names map to subdirectories. import a.b looks for a/b.th. A module that needs its own submodules imports them by their full dotted path, resolved against the same search paths.
Because the stdlib is last, a file called std.th in your own project shadows the standard library. That's how the std lib itself is found and overridden.