- Statically-typed, C-inspired: Variables and functions are statically typed; variables can be explicitly typed or inferred via
let. - Functions: Defined with
fun name(args) type { ... }. - Imports: Use
imp module;to import standard or user modules. Useimp module as alias;to import with a namespace alias. - Visibility: Prefix declarations with
pubto export them; declarations withoutpubare module-private. - Compounds: Custom types (like structs):
compound Point { num x; num y; }. - Quirks (Interfaces): Define required methods:
quirk Shape { area() num; }. - Implementations:
- Quirk implementation:
impl Rectangle as Shape { ... } - Plain compound methods:
impl Point { ... }
- Quirk implementation:
- Pattern Matching:
fit x { ... }for value-based branching. - Async/Await: Async functions are declared with
async fun ...; async calls must be awaited withawaitinside async functions. - Comments: Use
//for single-line comments.
- Primitive Types:
num: Signed 64-bit integer (maps to Cint64_t)dec: 64-bit floating-point (IEEE double; maps to Cdouble)f32: 32-bit floating-point (maps to Cfloat)f64: 64-bit floating-point (maps to Cdouble)i8,i16,i32,i64: Signed fixed-width integersu8,u16,u32,u64: Unsigned fixed-width integersiN,uN: Arbitrary-width signed/unsigned integersstr: Stringbin: Booleanchr: Characterraw: Opaque/"void" type (useraw*for C-stylevoid*)
- Arrays:
num[] arr = [1, 2, 3]; - Pointers:
Node* next;(self-referential and forward-declared types supported) - Type Inference: Supported for variables via
let name = expr;(initializer required).
letalways requires an initializer; the compiler infers the declared type from the expression.- Inference follows common literals and expressions:
- Numeric literals infer
numordecdepending on literal form. "text"infersstr,'c'inferschr,true/falseinfersbin.- Array literals infer
T[]from elements (for example[1, 2]->num[]). - Function calls infer the function's return type.
- Member access uses the receiver type (for example
Point p; let x = p.x;->num). - Indexing an array uses the element type (for example
let v = nums[i];->num).
- Numeric literals infer
- If the expression mixes numeric types, inference prefers the wider category (
decovernum).
- Declaration:
enum Color { Red, Green, Blue } - Longhand access:
Color.Red(works even if the enum is declared later in the file). - Shorthand access:
.Red(contextual; the expected enum type must be known from assignment, argument, orfit). - Assignments:
Color c = Color.Green;Color c = .Green;
- Function arguments:
fun takes(Color c) { ... }takes(.Blue);
- Pattern matching (
fit):fit c { .Red -> { ... }, .Green -> { ... }, _ -> { ... } }fit c { Color.Red -> { ... }, Color.Green -> { ... }, Color.Blue -> { ... } }
- Exhaustiveness: Missing enum variants in
fitmay emit a warning unless a_catch-all branch is present.
- If/Else: Standard conditional branching.
- Elif: Else-if chaining.
- Pattern Matching:
fitstatement for exhaustive and non-exhaustive matches. - Assert:
assert <condition>;aborts if condition is false. Optional message:assert <condition>, "msg";. - For Loops:
- Range:
for i : 0..10 { ... } - Array:
for item : arr { ... } - Indexed:
for i, item :: arr { ... } - While-style (condition):
for i < len { ... } - Infinite loop:
for true { ... } - Common in stdlib (for example
std/string.fn,std/net.fn, andstd/fs.fn).
- Range:
- Purpose: Run cleanup logic automatically when the current lexical scope exits.
- Order: LIFO (last
deferruns first). - Forms:
- Expression:
defer close(fd); - Block:
defer { log("done"); cleanup(); }
- Expression:
- Scope semantics:
- A
deferruns when the scope where it appears exits. - Function-scope defers run before
retand before implicit function end. - Loop-body defers run at the end of each iteration.
- On
continue/break, defers in the current loop iteration run before control leaves that iteration.
- A
- Block form:
asm { ... }; - String form:
asm "...";(use this for exact formatting/escapes) - Volatile:
asm volatile { ... };prevents reordering/elision - Architecture guard:
asm arch x86_64 { ... };(errors if target arch mismatches) - Operands & clobbers:
- Syntax:
asm volatile (out dst: "=r" = result; in src: "r" = value; clobber "rax", "memory") { ... };
- Reference named operands in templates using
%[name]. - Outputs are first, then inputs, then clobbers (GCC-style extended asm).
- Example (pass computed value):
num x = 21 + 21;num y = 0;asm volatile (out y: "=r" = y; in x: "r" = x) { mov %[x], %[y] };
- Example (pass computed value):
- Syntax:
- Notes:
- Asm block contents are preserved as raw text (including spaces, tabs, newlines, and comments).
- Fun does not validate assembly syntax inside asm blocks; correctness is decided by the downstream assembler/dialect (for example clang/GAS vs NASM differences).
- Use the string form when you need explicit escape control.
- Example (x86-style):
jmp $may fail under clang/GAS inline asm; label form like1: ... jmp 1bis typically more portable in that pipeline. - Supported arch names:
x86_64/amd64,x86/i386,aarch64/arm64,arm.
- Definition:
fun name(type arg, ...) return_type { ... } - Return: Use
ret value;to return from a function. - No Nested Functions: Functions cannot be declared inside other functions.
- Generic Functions:
fun id<T>(T x) T { ret x; }- Type arguments are inferred from call sites:
num v = id(1);.
- Type arguments are inferred from call sites:
- Async function declaration:
async fun name(args) type { ... } - Await usage:
awaitis valid only inside anasync funbody. - Required await: Calls to async functions and async quirk methods must be awaited.
- Await target: The awaited expression must resolve to an async call.
Example:
compound Counter {
num base;
}
quirk AsyncCounter {
async add(num x) num;
}
impl Counter as AsyncCounter {
async add(num x) num { ret self.base + x; }
}
async fun main() {
Counter c;
c.base = 41;
num out = await c.add(1);
_ = out;
}- Compounds: Like C structs, can have methods via
impl. - Quirks: Like interfaces/traits, define required methods.
- Impl: Attach methods to compounds or implement quirks for compounds.
- Method Dispatch: Quirk values can be used for dynamic dispatch (like trait objects).
- Quirk Method Visibility: Quirk methods follow normal visibility rules. Non-
pubmethods are callable inside the declaring module, but are not callable from importing modules. - Display Formatting (
{}): Formatting usesDisplay.to_string()only when that method is accessible at the call site. Ifto_string()is private in another module, formatting falls back to pointer-style output for that value.
- Standard Library:
imp std.c.io;maps to C standard headers. - Relative Imports:
imp relative.parent;for user modules. - Import Alias:
imp mod1 as one;then call symbols asone.some_fn(). - Duplicate Export Collisions: Import modules that export the same public symbol by aliasing each module and calling through the alias namespace.
- Example:
imp mod1 as one;imp mod2 as two;num a = one.pick();num b = two.pick();
- Example:
- Circular Dependency Detection: Compiler detects and errors on circular imports.
- C Macros: ALL_CAPS identifiers (e.g.,
NULL,INT_MAX) are allowed if the right header is imported. - Direct Mapping:
imp std.c.*;maps to C headers (stdio.h,limits.h, etc.). - Signature-only stdlib: Fun stdlib modules only declare signatures; C provides implementations.
- Printf formats:
numisint64_tin C. UsePRId64(from<inttypes.h>) or cast tolong longwith%lldwhen printing. - Compiler selection:
funuseszig ccby default. Override withFUN_CCand optionalFUN_CC_ARGS.
- std.io: file helpers +
print/println/print_num/print_dec/print_bin - std.sys: env access, process control (
sys_exit,sys_abort,sys_system), and PRNG wrappers - std.net: URL parsing, HTTP GET builder, and a best-effort local HTTP server launcher
- Note: std.net TCP/HTTP helpers use POSIX sockets via
std.c.net.
- Note: std.net TCP/HTTP helpers use POSIX sockets via
- std.option: Generic
Option<T>container withsome<T>/none<T>helpers. - std.result: Generic
Result<T>container withok<T>/err<T>helpers. - std.collections: Collection quirk helpers (len/is_empty).
- Type Checking: Errors for type mismatches, e.g., assigning
strtonum. - Undeclared Symbols: Errors for using undeclared variables or functions.
- Duplicate Declarations: Errors for redeclaring variables in the same scope.
- Missing Imports: Errors for importing non-existent modules.
- Incomplete Quirk Implementations: Errors if not all quirk methods are implemented.
- Stable warning IDs:
-
return_local_ptr-fit_non_exhaustive - Suppress next warning intentionally:
-
allow <warning_id>, "reason"; - Require next warning to appear:
-
expect <warning_id>, "reason"; - Scope:
allow/expectare statement directives and are valid inside function bodies. - Reason is required: the intent string documents why the warning is being allowed/expected.
- Behavior:
-
allowconsumes and suppresses the next emitted warning with that ID. -expectalso suppresses the next warning with that ID, but compilation fails if no such warning is emitted later.
Example:
fun bad() num* {
expect return_local_ptr, "tracked until allocator refactor";
num x = 1;
ret &x;
}
fun partial(bin x) {
allow fit_non_exhaustive, "legacy branch set, cleanup pending";
fit x {
true -> { }
}
}
fun main() {
partial(true);
num* p = bad();
_ = p;
}See also:
- examples/advanced/warning_allow.fn
- examples/advanced/warning_expect.fn
- examples/advanced/return_local_ptr_allow.fn
- examples/error_cases/warning_expect_unmet.fn
imp std.c.io;
compound Point { num x; num y; }
impl Point {
translate(num dx, num dy) {
self.x += dx;
self.y += dy;
}
}
fun main() {
Point p;
p.x = 1; p.y = 2;
p.translate(3, 4);
printf("p=(%d,%d)\n", p.x, p.y);
}- Forward Declarations: Compounds can reference each other regardless of order.
- Self-referential Types: Supported via pointers.
- Pattern Matching: Exhaustive and non-exhaustive with
_default branch. - CLI Tool: Compile, transpile, and run Fun code from the command line.
See the examples directory for real code and advanced usage.