Modules & Constants
This page covers two beginner questions:
- how do I split code across files?
- when should I use
letand when should I useconst?
Modules
Modules are just other .cneg files that you import.
import numbers as n;After importing, you use the module name or alias to reach public items:
fn:int main() {
return n.add(2, 3);
}Only public declarations are visible from another module.
Current import resolution order is:
- builtin
std.*modules - the project root, which is the directory containing the entry file
vendor/under that project root- a legacy fallback relative to the importing file's directory
That means if you run:
// src/main.cneg
import app.logic as logic;then app.logic resolves to src/app/logic.cneg. If you import vendorlib.echo, the loader next checks src/vendor/vendorlib/echo.cneg.
Canonical module names come from those root-relative paths, not just the file basename. So feature/helper.cneg and shared/helper.cneg stay distinct as feature.helper and shared.helper.
Current public forms are:
pfnfor functionspstructfor structspconstfor module-level constants
let vs const
This is the beginner version:
- use
letfor values created inside a function - use
constfor fixed values at module scope
let
let is for local values:
fn:int main() {
let x:int = 10;
return x;
}That value belongs to the function body.
const
const is for fixed values defined at module scope:
const LIMIT:int = 8;That value is not created fresh every time main runs. It is part of the module itself.
pconst
Use pconst when other modules should see the constant:
pconst BASE:int = 5;Then another file can use it:
import numbers as n;
const LOCAL:int = n.BASE + 4;Simple rule of thumb
Ask:
- “Is this value just part of one function?” -> use
let - “Is this a fixed named value for the whole file/module?” -> use
const - “Should another module be able to use it?” -> use
pconst
Small example
// numbers.cneg
pconst BASE:int = 5;
pfn:int add(a:int, b:int) {
return a + b;
}// main.cneg
import numbers as n;
const LOCAL:int = n.BASE + 2;
fn:int main() {
let value:int = n.add(LOCAL, 3);
return value;
}Constant rules
- constants are module-level only
- constant initializers must stay compile-time
- runtime calls are rejected in constant initializers
- runtime memory operations are rejected in constant initializers
- cyclic constant definitions are rejected
current compiler behavior
Constants are folded before LLVM lowering, so they behave like fixed compile-time values.
Common beginner mistakes
1. Trying to use const like let
This is not how const is used:
fn:int main() {
const X:int = 1;
return X;
}In cnegative, const is a module-level concept. Inside functions, use let.
2. Forgetting that imports only expose public items
If a function or constant is not declared with pfn or pconst, another module cannot use it.
Next step
Continue to Memory & Results.