docs / language
The fx language
fx is a systems language built for locality of reasoning: allocation, mutation, ownership, and I/O show up in the source, then lower to readable C on a small zspec substrate. Version 0.7.1 is already a full programming surface, not just “regions + vec.”
What fx is trying to be
- Explicit · no hidden allocator, no tracing GC, no magic control flow
- Dual emission · run natively and emit C a competent C programmer can maintain
- Tiered · ownership/regions (safe) → effects → optional refinement contracts
- C-native · wrap existing libraries; do not pretend C never existed
- Predictable for humans and agents · mechanical style, local decisions
Design pillars
1. Explicit effects
fn main() -> i32 effects { alloc, mut, io } {
// may allocate, mutate, and do I/O
}
Implemented effects: alloc, mut, io.
Pure functions omit the clause. Callers can see cost before reading the body.
2. Named regions
region r = arena(4096); // heap arena
region t = temp(1024); // short-lived heap batch
region s = scope; // stack borrow region (no heap)
region f = fx(4096); // hierarchical fx region
Full walkthrough: Regions and effects
(ownership, & / &mut / ®ion, all region kinds).
3. Dual emission
fx run/fx build· emit, link zspec, run or produce a binaryfx emit-c· inspect.c/.h--host· C ownsmain; fx is the library
4. Small but real standard library
std/ is ordinary fx you import. See
Standard library for APIs beyond vec.
Program structure
- Apps: usually
fn main() -> i32(exit code).mainmay also returnResult<i32, core_Err>. - Libraries:
module name;at the top of a.fxfile. - Imports:
import std/vec;· last path segment is the alias (vec.push). - Zspec symbols:
using core;(for idiomaticErr/core_Err). - C FFI:
extern "c" { fn name(…) -> …; }then link with--host/ link flags.
Types (0.7 surface)
| Category | What you can use |
|---|---|
| Integers | i8 i16 i32 i64 · u8 u16 u32 u64 |
| Floats | f32 f64 (unsuffixed float literals default to f64; use 1.5f32) |
| Other scalars | bool · string · void · core_Err |
| Aggregates | struct · enum (unit + payload variants) |
| Collections | Vec<T> · [T; N] · &[T] · StrBuilder · Map<string, i32> |
| Errors | Result<T, E> (usually core_Err) |
| Ownership | own T · &T · &mut T · ®ion r T |
| Generics | Functions and structs (fn new<T>(…), Box<T>, Pair<A,B>) · no traits |
Numeric rule of thumb: same-family ops only; i32↔i64 and f32↔f64 can widen;
no implicit signed↔unsigned or int↔float. Use postfix cast: x as u32.
Control flow and data
if/else,while, C-stylefor,break/continue- Exhaustive
matchon enums (including payload variants) defer stmt;for LIFO cleanup on scope exit- Struct literals
P { a: 1 }, field access, array indexa[i], slice reads[i]
enum Tok { Num(i32), End }
fn value(t: Tok) -> i32 {
return match t {
Num(n) => n,
End => 0,
};
}
Errors: Result and ?
using core;
fn parse_positive(n: i32) -> Result<i32, core_Err> {
if (n <= 0) {
return Err(CORE_ERR_INVALID_ARG);
}
return Ok(n);
}
fn main() -> Result<i32, core_Err> {
let v = parse_positive(10)?;
return Ok(v);
}
There is no Option: use Result.
Map lookup misses are errors and compose with ?.
Value-threading (important idiom)
Growing collections return an updated handle. Reassign the result
(same pattern for vec, map, strbuf):
v = vec_push(v, 40);
m = map_insert(m, "width", 30);
b = strbuf_push(b, "hello");
Prefer vec_get(v, i) for element access. There is no operator indexing on Vec
(v[i] is for arrays/slices only).
First programs
Minimal
fn main() -> i32 {
return 42;
}
fx new tiny --scaffold minimal
Recommended (visible heap + std/vec)
import std/vec;
fn main() -> i32 effects { alloc, mut } {
region r = arena(4096);
let v: Vec<i32> = vec.new(0);
let v2: Vec<i32> = vec.push(v, 40);
let v3: Vec<i32> = vec.push(v2, 2);
return vec.get(v3, 0) + vec.get(v3, 1);
}
fx new hello (default simple scaffold)
Arrays and slices
fn sum(s: &[i32]) -> i32 {
let mut acc: i32 = 0;
for (let i: i32 = 0; i < s.len; i = i + 1) {
acc = acc + s[i];
}
return acc;
}
fn main() -> i32 {
let table: [i32; 3] = [10, 20, 12];
let view: &[i32] = &table;
return sum(view); // 42
}
Working with C
fx run examples/showcase_wrap/compute.fx --host examples/showcase_wrap/host.c
See Wrap / C host for extern "c" and linking.
What not to expect (yet)
- Traits / interfaces / closures / iterators
Option<T>(useResult)- Generic
MapbeyondMap<string, i32>; map iteration API - Mutable slices or subrange slicing
a[lo..hi] - Package manager / large application ecosystem
- Direct Rust/Go/Zig FFI (C ABI only; others speak C)
Compact lookup: Reference (0.7 surface) · Regions · Std · CLI