Comptime evaluation
Optional compile-time evaluation — lookup tables, string routing, and generated constants with zero runtime cost. Nyra does not force comptime; use normal runtime code by default and opt in when you need Zig-style power.
Philosophy
The compiler folds pure expressions before codegen. Folded helpers are stripped from the binary; only the resulting constants (or imported pub const values) remain. Both zero-types and explicit types styles work — every example ships as foo.ny + foo.typed.ny under examples/toolchain/.
Three forms
1 — File-level comptime module
Put comptime on the first line. The entire file is evaluated at compile time. Export pub const (and optional pub struct / pub enum) for importers. No main, print, spawn, async, or extern in comptime modules.
comptime
fn hash_step(n) {
return (n * 2654435761) % 2147483647
}
pub const SEED = hash_step(42)
pub const SUM_FOUR = sum_array([0, 1, 2, 3])comptime
fn hash_step(n: i32) -> i32 {
return (n * 2654435761) % 2147483647
}
pub const SEED = hash_step(42)
pub const SUM_FOUR = sum_array([0, 1, 2, 3])Check without codegen: nyra check examples/toolchain/comptime_tables.ny. Import from runtime code:
import "comptime_tables.ny" as tables
fn main() {
let seed = tables::SEED
}import "comptime_tables.ny" as tables
fn main() -> void {
let seed = tables::SEED
}Example: examples/toolchain/comptime_import_main.ny.
2 — Function attribute #[comptime]
Mark a single function in a normal file. Calls with known arguments fold at compile time; the function body is removed from the runtime binary.
#[comptime]
fn mix(n) {
return n * 3
}
const SEED = mix(14)
fn main() {
print("SEED", SEED) // 42 — mix() not in binary
}#[comptime]
fn mix(n: i32) -> i32 {
return n * 3
}
const SEED = mix(14)
fn main() -> void {
print("SEED", SEED) // 42 — mix() not in binary
}Example: examples/toolchain/comptime_fn_attr.ny.
3 — Block expression comptime { … }
Folds a compile-time block to a value (trailing expression or return). Supports loops, mutable locals, and match.
const TOTAL = comptime {
let mut acc = 0
let mut i = 0
while i < 4 {
acc = acc + i
i = i + 1
}
acc
}
enum Mode { Fast Slow }
const SCORE = comptime {
match Mode.Fast {
Mode.Fast => 100
Mode.Slow => 10
}
}const TOTAL = comptime {
let mut acc: i32 = 0
let mut i: i32 = 0
while i < 4 {
acc = acc + i
i = i + 1
}
acc
}
enum Mode { Fast Slow }
const SCORE = comptime {
match Mode.Fast {
Mode.Fast => 100
Mode.Slow => 10
}
}Examples: comptime_block_loops.ny, comptime_match.ny, comptime_struct_enum.ny.
Power example — tables, strings, match
examples/toolchain/comptime_power.ny builds a lookup table, routes HTTP verbs by string match, and concatenates labels — all at compile time:
comptime
fn build_lookup(size) {
let mut table = [0; size]
let mut i = 0
while i < size {
table[i] = i * i
i = i + 1
}
return table
}
pub const LOOKUP = build_lookup(8)
pub const GET_CODE = comptime {
match "GET" {
"GET" => 1
"POST" => 2
_ => 0
}
}comptime
fn build_lookup(size: i32) -> i32 {
let mut table = [0; size]
let mut i: i32 = 0
while i < size {
table[i] = i * i
i = i + 1
}
return table
}
pub const LOOKUP = build_lookup(8)
pub const GET_CODE = comptime {
match "GET" {
"GET" => 1
"POST" => 2
_ => 0
}
}What comptime supports
- Integers, bools, strings (literals,
+,==/!=) - Fixed arrays (
[1, 2, 3],[x; N], spreads),.len()on arrays/strings - Structs — literals, field access, spread, struct
match - Enums — unit and payload variants
- Tuples and tuple
match for i in 0..N,for x in arr,while,break,continuematchon enums, bools, integer literals, string literals, guards, struct/tuple arms; or-patterns via desugar- Mutable updates:
table[i] = v,s.field = vwithlet mut - Generic calls (monomorphized before eval), pure function calls,
if/return
Forbidden in comptime
main,print,spawn,async,extern,unsafe,asm,parallel for- Runtime calls to
#[comptime]functions (compile error) - Reassigning non-
mutletbindings
Use priv const for internal comptime helpers; export only what importers need as pub const.
let vs const vs comptime
let | let mut | const | comptime file | #[comptime] | |
|---|---|---|---|---|---|
| When evaluated | Runtime | Runtime | Compile time (literal) | Compile time (whole file) | Compile time (call sites) |
| In binary | Yes | Yes | Constant only | Exported constants only | Folded away |
| Example | let n = 1 | let mut i = 0 | const MAX = 100 | pub const TABLE = … | #[comptime] fn hash(n) … |
See also Nyra Constants for everyday const usage.
Toolchain
| Command | Job |
|---|---|
nyra check path/to/comptime_module.ny | Type-check a comptime-only file (no main, no codegen required) |
nyra run importer.ny | Compile runtime code that imports folded constants |
nyra test tests/nyra/comptime/ | Run comptime regression suite (zero-types + typed pairs) |
Examples & tests
| Path | Demonstrates |
|---|---|
examples/toolchain/comptime_tables.ny | File-level module + hash helpers |
examples/toolchain/comptime_import_main.ny | Import pub const from comptime file |
examples/toolchain/comptime_fn_attr.ny | #[comptime] function folding |
examples/toolchain/comptime_block_loops.ny | comptime { } + while |
examples/toolchain/comptime_match.ny | Enum / bool / integer match |
examples/toolchain/comptime_struct_enum.ny | Struct literals + struct match |
examples/toolchain/comptime_power.ny | Tables, string match, concat |
examples/toolchain/metaprogramming.ny | Comptime + macros + struct JSON synthesis |
tests/nyra/comptime/ | Full test grid (*_test.ny runners) |
Metaprogramming (overview)
Nyra metaprogramming runs during compilation — no runtime reflection cost on hot paths:
- Comptime — file modules,
#[comptime]functions, andcomptime { }blocks fold values before codegen - Macros — hygienic expression substitution before typecheck (traits & macros)
- Generics — monomorph specialization (generics)
- Struct JSON — compiler synthesizes
{Struct}_json_encode/_json_decodefor eligible structs (examples/struct_serde.ny)
See examples/toolchain/metaprogramming.ny and stdlib/meta/mod.ny.