Modules
Achronyme supports splitting code across multiple .ach files using modules. Export declarations to make them available, and import modules by namespace or by name.
Exporting Declarations
Section titled “Exporting Declarations”Inline Exports
Section titled “Inline Exports”Use export before fn or let to make a declaration available to other files.
export fn add(a, b) { a + b }export fn mul(a, b) { a * b }export let PI = 3Only fn and let can be exported. Mutable variables (mut), public, and witness declarations cannot be exported.
// These are errors:export mut x = 5 // ✗ cannot export mutexport public y // ✗ cannot export publicExport Lists
Section titled “Export Lists”You can also collect exports at the end of a file using export { ... }:
fn add(a, b) { a + b }fn mul(a, b) { a * b }let PI = 3
export { add, mul, PI }This is equivalent to marking each declaration with export individually. You can mix both styles in the same file. Exporting the same name twice (inline + list, or listed twice) is an error.
Declarations without export are private to the file. They can be used by exported functions internally but are not accessible from outside.
fn internal_helper(x) { x * x } // private
export fn square(x) { internal_helper(x) // works inside this file}Importing a Module
Section titled “Importing a Module”Namespace Import
Section titled “Namespace Import”Use import "path" as alias to load a module. The alias is mandatory and becomes the namespace for accessing the module’s exports.
import "./math.ach" as math
print(math.add(1, 2)) // 3print(math.PI) // 3Access exported values using dot notation: alias.name.
Selective Import
Section titled “Selective Import”Use import { name1, name2 } from "path" to import specific names directly into scope, without a namespace prefix:
import { add, PI } from "./math.ach"
print(add(1, 2)) // 3 — no prefix neededprint(PI) // 3Selective imports copy the value at import time (snapshot semantics). Changes to the original module’s globals after import are not reflected.
If a requested name is not exported by the module, the compiler suggests the closest match:
error: module "math.ach" does not export `ad`. Did you mean `add`?You can combine both styles for the same module:
import { add } from "./math.ach"import "./math.ach" as math
print(add(1, 2)) // selective — no prefixprint(math.mul(3, 4)) // namespace — with prefixModule Paths
Section titled “Module Paths”Paths are relative to the importing file and must include the .ach extension.
import "./utils.ach" as utils // same directoryimport "../lib/hash.ach" as hash // parent directoryimport "./crypto/poseidon.ach" as pos // subdirectoryAbsolute paths are not supported. All imports use relative paths.
Transitive Imports
Section titled “Transitive Imports”Modules can import other modules. The chain is resolved automatically.
export fn fa() { 1 }
// b.achimport "./a.ach" as aexport fn fb() { a.fa() + 1 }
// c.achimport "./b.ach" as bprint(b.fb()) // 2Circular Imports
Section titled “Circular Imports”Circular dependencies are detected and produce an error.
import "./y.ach" as y // error: CircularImport
// y.achimport "./x.ach" as xModules in Circuits
Section titled “Modules in Circuits”Imports work with ach circuit the same way as ach run. Imported functions are inlined at each call site.
export fn my_hash(a, b) { poseidon(a, b) }
// circuit.achimport "./hash_lib.ach" as h
circuit hash_check(out: Public, a: Witness, b: Witness) { assert_eq(h.my_hash(a, b), out)}Errors
Section titled “Errors”| Error | Cause |
|---|---|
ModuleNotFound | The file path does not resolve to an existing file |
CircularImport | Two or more modules import each other |
ModuleLoadError | The file was found but contains parse errors |
DuplicateModuleAlias | Two different files imported with the same alias |
| Name not exported | import { x } where x is not exported by the module (with “did you mean?” suggestion) |
| Duplicate export | The same name is exported more than once (inline + list, or listed twice) |
| Import conflict | A selectively imported name conflicts with an existing global or a prior import from a different module |
| Export list undefined | export { x } where x is not defined in the module |
Warnings
Section titled “Warnings”| Code | Cause |
|---|---|
| W005 | A selectively imported name is never used (prefix with _ to suppress) |
Restrictions
Section titled “Restrictions”importandexportare only allowed at the top level (not inside functions or blocks)import,export, andasare reserved keywordsfromis not a keyword — it is recognized contextually only afterimport { ... }, so it can be used as a variable name elsewhere- Selective imports use snapshot semantics (the value is copied at import time)
- Re-exports (
export import) are not supported - Importing the same file with two different aliases is allowed (the file is parsed once)
Quick Reference
Section titled “Quick Reference”| Syntax | Description |
|---|---|
export fn name() { ... } | Export a function (inline) |
export let X = value | Export a constant (inline) |
export { a, b, c } | Export multiple names (list) |
import "./file.ach" as alias | Namespace import |
import { a, b } from "./file.ach" | Selective import |
alias.name | Access a namespace binding |