The language for autonomous distributed software systems — AI agents, durable workflows, and resilient services.
Website • Nulang Cloud • GitHub
Nulang is the language for building software that keeps running — across failures, restarts, and node boundaries — without constant human intervention.
If you are building AI agents that need to remember state, durable workflows that must survive crashes, or distributed services that stay available under load, Nulang gives you a single, coherent foundation instead of a pile of bolted-on libraries.
- Fault tolerance by default — supervision trees, links, and monitors turn crashes into recoverable events, not outages.
- Distribution without rewiring — actors and messages work the same whether they run on one node or a cluster.
- Durable execution — workflows checkpoint state and resume after restarts with saga compensation for failures.
- AI-native agents — first-class
agentdeclarations with LLM clients, memory, and multi-agent teams. - Memory safety without runtime pauses — reference capabilities and per-actor ORCA GC keep you safe while actors stay responsive.
- Compile once, run anywhere — bytecode, native AOT, or WASM backends from the same source.
| Feature | Description |
|---|---|
| Actor Model | Lightweight actors with cooperative scheduling, work-stealing queues, and supervision trees that isolate and recover from failures |
| Algebraic Effects | First-class effect system with perform/handle/resume semantics |
| Capability System | Fine-grained reference permissions (iso/trn/ref/val/box/tag/lineariso) for memory safety |
| AI Agents | First-class agent declarations with LLM clients, memory, and multi-agent teams |
| Distributed Runtime | Location-transparent actor messaging so you can scale from one node to a cluster without rewriting code |
| ORCA GC | Per-actor concurrent garbage collection with cycle detection |
| CRDTs | 8 conflict-free replicated data types for shared distributed state |
| Register-Based VM | High-performance bytecode VM with NaN-tagged value representation |
| Cranelift JIT Backend | Tiered execution: interpreter for cold code, JIT compilation for hot loops |
| Native/AOT Backend | Ahead-of-time compilation to native object code via Cranelift |
| BEAM/OTP Primitives | link/monitor/exit/trap_exit/registry via perform Actor.*, spawn link/spawn monitor, selective receive with after timeout, actor priority (Actor.set_priority), timers, process groups |
| SIMD Vectorization | Auto-vectorization of array loops via Cranelift SIMD (I64x2, F64x2, I32x4, F32x4) + WASM SIMD backend |
| WASM Backend | MIR→WASM compiler (wasm-encoder) + Wasmtime host runtime with guard pages, inlining, SIMD, and AOT compilation |
| Python Interop | Native Actor pattern: Python isolated to dedicated OS threads, marshal-only boundary |
| Unbounded Mailboxes | Lock-free MPSC queues (crossbeam::SegQueue) — BEAM-semantics, no message loss |
| Stress Test Suite | 30 chaos tests for supervision, scheduler fairness, GC, persistence, CRDTs, and JIT fallback under load |
Nulang is Alpha — but not a greenfield project. The compiler pipeline, VM and JIT, actor runtime, supervision, effects, capabilities, distribution, durability, and AI runtime all exist and are tested today:
- ✅ All 1362 tests pass with
cargo test(1368 with--features wasm-backend) - ✅ Builds with
cargo build - ✅ i64-tagged
Valuerepresentation with distinct high-16 type tags (canonical constants insrc/value_layout.rs) — immune to WASM NaN canonicalization - ✅ 138-opcode bytecode ISA (arithmetic, control flow, closures, objects, effects, actors, FFI, Python, distribution)
- ✅ Hindley-Milner type inference with algebraic effects, user-declared variant types (construction + recursive pattern matching with guards), and row-polymorphic records (
fn(r) r.x + r.yaccepts any record withxandy; closed record annotations stay exact) - ✅ Actor runtime: spawn,
spawn link/spawn monitor, send, monitors, links, supervision, timers, registry, process groups, selectivereceivewithafter, actor priority - ✅ ORCA-style per-actor GC with cycle detection
- ✅ AI runtime:
agentdeclarations, LLM providers (OpenAI, Ollama), memory, pipelines, debates, supervisor teams - ✅ Durable workflow runtime:
workflowdeclarations with steps, timers, signals, saga compensation
- Rust (stable channel, 1.93+)
- Python 3 development headers, for the default build (see Feature flags to skip this)
- Linux or macOS (Windows support planned)
git clone https://github.com/dporkka/nulang.git
cd nulang
cargo build --releaseThree optional subsystems are on by default so a plain cargo build behaves
as before this flag set existed. Build without them for a leaner binary and
fewer system dependencies:
| Feature | Enables | Off by default? |
|---|---|---|
python |
PyO3 Python interop (src/python/) |
No — on by default |
sqlite |
libsql/Turso persistence (persistence.rs) |
No — on by default |
lsp |
tower-lsp language server (src/lsp/) |
No — on by default |
wasm-backend |
WASM compiler (mir_wasm.rs) + Wasmtime runtime (wasm_runtime.rs), --backend wasm|wasm-run|wasm-aot |
Yes — requires wasmtime CLI for AOT |
# Build with WASM backend enabled:
cargo build --release --features wasm-backend# Skip PyO3, libSQL, and the LSP server entirely:
cargo build --release --no-default-features
# Pick just what you need:
cargo build --release --no-default-features --features sqlitecargo test# Compile and run a file (bytecode backend, default)
cargo run -- myprogram.nula
# Type-check only
cargo run -- --check myprogram.nula
# Evaluate a string
cargo run -- --eval 'perform IO.print("Hello")'
# Native/AOT backend: compile to native code via Cranelift
cargo run -- --backend native myprogram.nula
# WASM backend: compile to out.wasm
cargo run --features wasm-backend -- --backend wasm myprogram.nula
# WASM backend: compile and run via Wasmtime
cargo run --features wasm-backend -- --backend wasm-run myprogram.nula
# WASM backend: compile to .wasm + AOT .cwasm (requires wasmtime CLI)
cargo run --features wasm-backend -- --backend wasm-aot myprogram.nula
# Package manager (also: nula build-wasm for WASM AOT builds)
cargo run -- nula new my-appRunnable programs live in examples/:
cargo run -- examples/fibonacci.nula # closures + recursion
cargo run -- examples/effects.nula # algebraic effect handlers
cargo run -- examples/counter_actor.nula # actor declaration + spawn
cargo run -- examples/variant_option.nula # user-declared variant types (Option)IO.print is handled by the standalone VM's built-in effect (every snippet
below was verified with cargo run):
perform IO.print("Hello, World!")
From examples/fibonacci.nula:
let fib = fn(n) {
if n <= 1 then n else fib(n - 1) + fib(n - 2)
} in fib(10)
From examples/counter_actor.nula:
actor Counter {
state count = 0
behavior get() { self.count }
behavior inc() { self.count + 1 }
}
spawn Counter { count = 0 }
From examples/effects.nula:
handle perform Math.getAnswer() {
| Math.getAnswer() => 42
}
agent Assistant = {
model: "gpt-4o",
system_prompt: "You are helpful.",
memory: { max_turns: 10 }
}
let a = spawn Assistant {} in
ask a ask("What is an actor model?")
let s = "hello" in
match s {
| "hello" => 1
| _ => 0
}
Arms may carry a guard (| pat if cond => body — a failing guard falls
through to the next arm), and patterns nest recursively: Some(Some(x)),
tuple, and record sub-patterns each test the positions they name.
let inc = fn(x) { x + 1 } in
let dbl = fn(x) { x * 2 } in
1 |> inc |> dbl
+-------------------------+
| Source Code |
+-------------------------+
|
v
+----------+ +-------------------------+
| Lexer |--->| Parser (AST) |
+----------+ +-------------------------+
|
v
+-------------------------+
| Type Checker (H-M) |
| Effect Checker |
| Capability Analyzer |
+-------------------------+
|
v
+-------------------------+
| HIR → MIR Lowering |
+-------------------------+
|
+----------------+----------------+
| | |
v v v
+------------------+ +------------------+ +------------------+
| Bytecode Backend | | Native/AOT | | WASM Backend |
| (138 opcodes) | | (Cranelift) | | (wasm-encoder) |
+------------------+ +------------------+ +------------------+
| | |
v v v
+------------------+ +------------------+ +------------------+
| Register VM + | | Native Binary | | Wasmtime Runtime |
| Cranelift JIT | | (AOT compiled) | | (WASM execution) |
+------------------+ +------------------+ +------------------+
|
v
+-------------------------+
| Actor Runtime |
| (Spawn/Send/Receive/ |
| Links/Monitors) |
+-------------------------+
|
+---------+-------------------------------+
| | |
v v v
+--------+ +--------+ +-----------+
| Sched | | ORCA | |Distributed|
| (Work | | GC | | Runtime |
| Steal) | |(Per- | |(TCP,CRDT) |
+--------+ | Actor) | +-----------+
+--------+
|
+---------+---------+
| |
v v
+----------+ +---------------+
|Supervisor| | CRDT Manager |
| (OTP) | | (8 CRDT types)|
+----------+ +---------------+
| Module | Description | Lines |
|---|---|---|
lexer |
Hand-written state machine, indentation-based tokenization | ~1,300 |
parser |
Recursive descent with Pratt precedence climbing | ~4,385 |
ast |
Abstract syntax tree definitions (30+ expression types) | ~940 |
types |
Type system, capability lattice, effect rows, error types | ~975 |
typechecker |
Hindley-Milner Algorithm W with full inference | ~3,890 |
effect_checker |
Algebraic effect row checking + capability analysis | ~3,035 |
hir / hir_lower |
High-level IR and AST → HIR lowering | ~2,700 |
mir / mir_lower |
Mid-level IR and HIR → MIR lowering | ~3,550 |
mir_codegen |
MIR-to-bytecode compilation with register allocation | ~2,250 |
bytecode |
138 opcodes, 32-bit fixed-width instructions | ~1,050 |
value_layout |
Canonical NaN-boxing tag/mask constants (single source of truth) | ~300 |
vm |
Register-based virtual machine, effect handlers, JIT tiering hook | ~4,670 |
aot/mod + aot/codegen |
AOT native compiler: MIR → Cranelift object code | ~1,270 |
type_metadata |
Type metadata for typed JIT and AOT compilation | ~125 |
wasm_types |
WASM component model type definitions | ~100 |
wasm_component_runtime |
WASM component runtime (WIP) | ~335 |
mir_wasm |
MIR → WASM compiler via wasm-encoder (wasm-backend feature) | ~690 |
wasm_runtime |
Wasmtime host runtime for WASM modules | ~290 |
jit/mod |
JIT session manager, tiered execution, hot-counter tracking | ~570 |
jit/compiler |
Bytecode → Cranelift IR (50 opcodes) | ~1,005 |
jit/typed_compiler |
Type-directed JIT: direct CLIF when operand types are known | ~2,105 |
jit/simd_analyzer / jit/simd_compiler |
Vectorizable-loop detection + SIMD CLIF emission | ~3,185 |
jit/runtime |
NaN-tag-aware runtime helpers for JIT (31 extern C functions) | ~375 |
runtime/mod |
Runtime coordinator: actors, scheduling, GC, supervision, distribution | ~5,870 |
runtime/actor |
Actor struct, lifecycle, state management | ~520 |
runtime/scheduler |
Work-stealing queues + reduction-bounded cooperative scheduler | ~570 |
runtime/mailbox |
Unbounded lock-free MPSC via crossbeam SegQueue | ~355 |
runtime/timer |
Hierarchical timer wheel for send_after, exit_after, kill_after | ~505 |
runtime/registry |
Local actor name registry (register/whereis/registered) | ~210 |
runtime/process_groups |
Decentralized actor group membership (Erlang pg) | ~285 |
runtime/heap |
Per-actor bump allocator with ORCA object headers | ~1,670 |
runtime/gc |
ORCA reference counting (3-count protocol) | ~1,420 |
runtime/orca_cycle |
Intra-node cycle detector with weighted heuristic | ~1,680 |
runtime/supervisor |
Erlang/OTP-style supervision strategies | ~745 |
runtime/network |
TCP transport, NUL0 wire protocol | ~2,250 |
runtime/cluster |
Gossip-based cluster membership + failure detection | ~1,235 |
runtime/distributed |
Location-transparent actor addressing | ~1,940 |
runtime/crdt |
CRDT trait + GCounter, PNCounter, GSet, ORSet, AWORSet | ~1,365 |
runtime/crdt_reg |
LWWRegister, MVRegister, RGA sequence CRDT | ~875 |
runtime/crdt_manager |
CRDT factory, sync ops, inter-node merge | ~1,160 |
runtime/persistence |
Snapshot/journal stores (MemoryStore, JsonFileStore, LibsqlStore) | ~1,190 |
python/bridge + python/marshal |
PyO3 interpreter bridge + Value↔Python marshalling | ~1,290 |
ffi |
C-compatible FFI layer, native-library registry, embedder C API | ~1,440 |
ai |
LLM providers (OpenAI, Ollama), memory, pipelines, debates, supervisor teams | ~2,590 |
lsp |
tower-lsp language server (12 features incl. hover, inlay hints, completion) | ~1,935 |
package |
Nula package manager (manifest, lockfile, resolver, commands) | ~1,120 |
docgen |
Documentation generator: .nula doc comments → docs/api.md | ~420 |
stdlib |
Standard-library inventory documenting built-in effects and functions | ~470 |
repl |
Interactive REPL with :type, :ast, :bytecode commands | ~720 |
main |
CLI entry point (run, repl, eval, check, lsp, backend selection) | ~695 |
integration_tests / stress_tests / runtime/tests |
End-to-end pipeline, chaos, and runtime test suites | ~12,050 |
Total: ~88,100 lines of Rust across 85 source files with 1329 tests.
Nulang uses the ORCA (Optimized Reference Counting Architecture) protocol from Pony for memory management. Each actor has its own heap, and garbage is collected without global stop-the-world pauses.
| Component | Description |
|---|---|
ActorHeap |
Bump allocator with 5 size-class free lists and live-object tracking |
OrcaGc |
Three-count reference counting (local/foreign/sticky) |
OrcaCoordinator |
Routes cross-actor reference operations between nodes |
CycleDetector |
Weighted-heuristic DFS cycle detection with trial decrements |
Actors can communicate across machine boundaries with location-transparent messaging.
| Component | Description |
|---|---|
NetworkTransport |
TCP-based transport with NUL0 binary wire protocol |
ClusterState |
Gossip-based membership with heartbeat failure detection |
ActorAddress |
Location-transparent addressing (local or remote) |
AddressResolver |
Resolves addresses to local actors or network routes |
API:
rt.enable_distribution("0.0.0.0:7878".parse()?)?;
rt.join_cluster("192.168.1.100:7878".parse()?);
rt.send_distributed(ActorAddress::remote(node_id, actor_id), "hello", &[]);
rt.process_network(); // handle incoming packetsEight conflict-free replicated data types enable actors to share mutable state across nodes without coordination.
| CRDT | Type | Operations | Use Case |
|---|---|---|---|
GCounter |
Counter | increment | Page views, likes |
PNCounter |
Counter | increment, decrement | Inventory, voting |
GSet |
Set | insert | Tags, followers |
ORSet |
Set | add, remove (add-wins) | Shopping cart |
AWORSet |
Set | add, remove (timestamp) | Collaborative todo |
LWWRegister |
Register | write | Profile name, config |
MVRegister |
Register | write (multi-value) | Conflict detection |
RGA |
Sequence | insert, delete | Collaborative text |
API:
let (id, _) = rt.crdt_manager.as_mut().unwrap().create_gcounter();
rt.crdt_manager.as_mut().unwrap().get_gcounter_mut(id).unwrap().increment_by(5);
rt.sync_crdts(); // broadcast to all connected nodes35+ Erlang/OTP primitives analyzed and the core set implemented. The primitives are
reachable from Nulang source as built-in effects — perform Actor.link(t),
Actor.unlink(t), Actor.monitor(t), Actor.demonitor(t), Actor.trap_exit(flag),
Actor.exit(reason), Actor.register(name), Actor.unregister(name),
Actor.whereis(name), Actor.set_priority(0|1|2) — dispatched via
ActorVmCallbacks::perform_builtin_effect into Runtime::perform_actor_builtin
(nil no-op outside an actor). The legacy monitor/demonitor/link/unlink/exit/yield
VM opcodes remain defined but unhandled — superseded by the effect surface.
| Primitive | File | Description |
|---|---|---|
receive |
parser.rs, vm.rs |
Selective receive: scans the mailbox in FIFO order for the first message matching any arm (OpCode::ReceiveMatch → ActorVmCallbacks::try_receive_match), binds payload values to arm params, non-matching messages stay queued; no-match falls back to pop-any (nil when empty). The timed form receive { arms } after ms => body (OpCode::ReceiveWait 0xA0) suspends the actor until a match arrives or the timeout fires. See examples/receive.nula. |
spawn link / spawn monitor |
parser.rs |
Spawn-and-link/monitor in one step: parser desugars to spawn + perform Actor.link/Actor.monitor on the spawner |
monitor |
runtime/mod.rs |
Watcher monitors target actor for exit (Actor.monitor builtin or Rust API) |
demonitor |
runtime/mod.rs |
Remove a monitor (Actor.demonitor builtin or Rust API) |
link/unlink |
runtime/mod.rs |
Bidirectional fault tolerance links (Actor.link/Actor.unlink builtins or Rust API) |
exit |
runtime/mod.rs |
Typed actor exit with ExitReason enum (Actor.exit builtin or Rust API) |
trap_exit |
runtime/actor.rs |
Convert exit signals to messages (Actor.trap_exit builtin) |
register/whereis |
runtime/registry.rs |
Local actor name registry (Actor.register/unregister/whereis builtins) |
set_priority |
runtime/actor.rs, runtime/scheduler.rs |
`Actor.set_priority(0 |
send_after |
runtime/timer.rs |
Hierarchical timer wheel |
pg process groups |
runtime/process_groups.rs |
Decentralized actor groups |
yield |
runtime/actor.rs |
Cooperative scheduling yield via reduction quotas |
Three high-ROI changes implemented in parallel:
| # | Proposal | Change | Impact |
|---|---|---|---|
| 2.1 | Lock-free mailboxes | crossbeam::SegQueue |
ABA-safe, cache-line optimized |
| 4.2 | Linear type moves | Capability::LinearIso + consumption tracking |
Zero-cost iso sends |
Tiered execution system with Cranelift 0.132:
| Component | Description |
|---|---|
JitSession |
Manages Cranelift JIT module, compiled function cache |
compiler.rs |
Bytecode → CLIF for 50 opcodes (arith, compare, control flow) |
runtime.rs |
31 extern "C" NaN-tag-aware runtime helpers |
| Tiered execution | Interpreter (cold) → JIT compile (hot threshold: 1,000) |
| Graceful fallback | Unsupported opcodes → continue interpreting |
Type Guard Stripping (proposal 1.2): When the typechecker knows a register holds Int or Float, the JIT emits direct CLIF instructions (iadd, fadd) instead of calling NaN-tag-aware runtime helpers. Eliminates ~30% of overhead in numeric loops.
| Component | Description |
|---|---|
typed_compiler.rs |
Type-directed JIT with TypeMetadata / KnownType |
| Inline sext48 | Sign-extend in ~5 CLIF instructions (was: runtime call) |
| Typed binops | Direct iadd/fadd/imul when operand types known |
| Fallback | Unknown types → same runtime helpers as v0.9 |
LSP Inlay Hints (proposal 6.1): Language Server Protocol support with inline type annotations.
| Component | Description |
|---|---|
lsp/mod.rs |
tower-lsp server with textDocument/inlayHint |
| Type inlays | let x = 42 shows : Int after x |
| Capability inlays | let y :iso String highlights :iso |
| Effect inlays | fun f() ! IO shows [IO] |
Auto-vectorization of element-wise array loops using Cranelift SIMD instructions:
| Component | Description |
|---|---|
simd_analyzer.rs |
Pattern detection for vectorizable loops (c[i] = a[i] + b[i]) |
simd_compiler.rs |
SIMD CLIF emission (I64x2, F64x2, I32x4, F32x4) |
| 8 vectorization checks | Uniform access, no loop-carried deps, no calls in loop, etc. |
| Scalar prefix/epilogue | Handles trip_count % vector_width elements individually |
is_simd_supported() |
Runtime CPU feature detection (SSE2/NEON) |
| Tiered integration | CompiledSimdAndRan action in tiered execution |
| ISA flag | enable_simd = true in Cranelift settings |
An external technical audit identified several architectural risks. The following corrections were applied:
Reverted: Dual-Region Heaps + Escape Analysis
| Audit Finding | Risk | Action |
|---|---|---|
| Generational nursery + ORCA foreign-ref tracking | Cross-actor pointer rewriting during minor GC is a massive corruption vector | Reverted to pure ORCA per-actor heap |
| Escape analysis without generational GC | Vestigial — no runtime benefit without nursery | Removed from build |
Bounded mailboxes (ArrayQueue) |
Violates BEAM semantics — supervisor signals can be dropped | Switched to crossbeam::SegQueue (unbounded) |
| Centralized cycle detector (1,550 lines) | Distributed DFS over TCP misidentifies slow refs as dead cycles | Restricted to intra-node only |
| Deep Python integration (TAG_PYTHON in VM) | CPython global mutable state leaks into clean Rust runtime | Replaced with Native Actor pattern |
Philosophy: Optimize pure ORCA first. Layer generational GC only after ORCA is provably correct under chaotic conditions. Layer Python interop only via isolated native actors with marshal-only boundaries.
Python interop is the critical path for AI adoption. After architectural audit, the design shifted from deep VM integration to the Native Actor pattern — Python runs only in dedicated OS threads with marshal-only data crossing.
// Python interop via native actors (isolated, explicit marshal)
let result = perform Python.call("torch", ["Tensor"], [[1.0, 2.0, 3.0]])
perform IO.print(result) // marshaled Float value: 6.0
| Component | Description |
|---|---|
python/bridge.rs |
PyO3 interpreter bridge with GIL management |
python/marshal.rs |
Bidirectional Nulang Value ↔ Python object conversion |
| Enforced isolation | No Python objects in Nulang VM — marshal at boundary |
| 8 Python opcodes | 0x94-0x9B (PyImport, PyCall, PyToNu, …) defined and dispatched by the VM |
| 21 tests | Registry, import, call, marshal round-trips |
Stress test suite (30 chaos tests in src/stress_tests.rs) deliberately breaks the runtime under load — actor-effect boundary, supervision, GC, persistence, CRDTs, and JIT fallback:
| Test | Scenario |
|---|---|
stress_slow_worker_with_mailbox_flood |
Slow Worker + Mailbox Flood |
stress_actor_crash_during_scheduling |
Actor Crash During Scheduling |
stress_cascading_exit_under_load |
Cascading Exit Under Load |
stress_monitor_during_rapid_spawn_exit |
Monitor During Rapid Spawn/Exit |
stress_scheduler_with_mixed_workload |
Scheduler with Mixed Workload |
stress_mailbox_never_drops_system_messages |
Mailbox Never Drops System Messages |
stress_orphaned_actor_cleanup |
Orphaned Actor Cleanup |
stress_reduction_quota_fairness |
Reduction Quota Fairness |
stress_effect_resume_after_mailbox_pressure |
Effect Resume After Mailbox Pressure |
stress_supervisor_crash_during_recovery |
Supervisor Crash During Recovery |
stress_registry_high_churn |
Registry High Churn |
stress_process_groups_membership_churn |
Process Groups Membership Churn |
stress_timer_wheel_overload |
Timer Wheel Overload |
stress_persistent_actor_checkpoint_recovery |
Persistent Actor Checkpoint / Recovery |
stress_crdt_counter_merge_stress |
CRDT Counter Merge Stress |
stress_crdt_manager_sync_ops |
CRDT Manager Sync Ops |
stress_monitor_spawn_storm |
Monitor Spawn Storm |
stress_jit_hot_loop_then_cold_fallback |
JIT Hot Loop Matches Interpreter |
stress_remote_actor_cache_lru_eviction |
Remote Actor Cache LRU Eviction |
stress_supervisor_restart_intensity |
Supervisor Restart Intensity |
stress_gc_foreign_ref_churn |
GC Foreign Reference Churn |
stress_distribution_local_fallback_when_disabled |
Distribution Local Fallback When Disabled |
stress_reduction_yield_under_pressure |
Reduction Yield Under Pressure |
stress_actor_heap_allocation_pressure |
Actor Heap Allocation Pressure |
stress_cascading_supervisor_shutdown |
Cascading Supervisor Shutdown |
stress_persistence_journal_replay_ordering |
Persistence Journal Replay Ordering |
stress_cycle_detector_epoch_gating |
Cycle Detector Epoch Gating |
stress_mailbox_system_priority_preservation |
Mailbox System Priority Preservation |
stress_trap_exit_with_monitor_storm |
Trap Exit With Monitor Storm |
stress_gc_cycle_detector_under_foreign_ref_load |
GC Cycle Detector Under Foreign Reference Load |
Design documents (see docs/archive/):
DESIGN_WORKFLOW_SDK.md— workflow SDK design (partially implemented in v0.8)DESIGN_PACKAGE_MANAGER.md— package manager design (implemented asnulaCLI)DESIGN_WEB_FRAMEWORK.md,DESIGN_CLOUD.md— design-only, not implemented
- Fault Tolerance First — Inspired by Erlang's "let it crash" philosophy with supervision trees
- Type Safety Without Ceremony — Strong static typing with Hindley-Milner full inference
- Effects as Values — Algebraic effects make computational context explicit
- Safe Sharing — Capabilities control reference permissions at the type level
- AI-Native — First-class support for LLM-powered agents as language primitives
- Zero-Cost Distribution — Actors naturally span nodes; CRDTs share state without coordination
This is an active implementation with the following components functional:
- Lexer (full token set, indentation handling)
- Parser (all expression types, declarations, actor/agent definitions)
- AST (complete node types)
- Hindley-Milner type checker (Algorithm W with full inference)
- Algebraic effect checker (effect row compatibility, capability analysis)
- Compiler (AST → HIR → MIR → bytecode with register allocation)
- Native AOT Compiler (MIR → Cranelift native object code)
- WASM Compiler (MIR → WASM via wasm-encoder)
- VM (register-based execution, arithmetic, comparisons, control flow)
- REPL (parse-typecheck-compile-execute cycle with introspection)
- Integration tests (227 end-to-end pipeline tests)
- Actor runtime (spawn, send, links, monitors, selective receive)
- Work-stealing scheduler (cooperative, reduction quotas)
- ORCA garbage collector (per-actor heap, 3-count protocol, cycle detection)
- Supervision trees (OneForOne, OneForAll, RestForOne restart strategies)
- Fault tolerance tests (27 supervisor/exit/link/monitor tests)
- Distributed runtime (TCP transport, cluster membership, location-transparent messaging)
- CRDT subsystem (8 types: counters, sets, registers, sequences)
- CRDT manager (factory, sync, inter-node merge)
- BEAM/OTP primitives (
perform Actor.*: monitor, link, exit, trap_exit, register, whereis, set_priority; send_after, pg, yield, selective receive withafter; legacy VM opcodes for monitor/link/exit/yield are defined but unhandled) -
spawn link/spawn monitor(parser desugar to spawn +Actor.link/Actor.monitor) - Unbounded mailboxes (crossbeam::SegQueue — BEAM semantics, no message loss)
- Hierarchical timer wheel (send_after, exit_after, kill_after)
- Actor registry (register/whereis/registered)
- Process groups (decentralized actor group membership)
- Linear type moves (compile-time
isoconsumption tracking) - Cranelift JIT backend (tiered execution, 50 opcodes, hot-counter threshold)
- Type guard stripping (direct CLIF when types known, ~30% speedup in numeric loops)
- LSP inlay hints (type/capability/effect annotations via tower-lsp)
- SIMD vectorization (auto-vectorize array loops: I64x2, F64x2, I32x4, F32x4)
-
Dual-region generational heapREVERTED (audit: ORCA+nursery corruption risk) -
Escape analysisREVERTED (audit: no benefit without nursery) - Python interop — Native Actor pattern (isolated OS threads, marshal-only boundary)
- Stress test suite (30 chaos tests: supervision, scheduler fairness, GC, persistence, CRDTs, JIT fallback)
- AI SDK design document (agent DSL, tool binding, memory, multi-agent)
- Web Framework design document (endpoints, controllers, channels, LiveView)
- Package Manager (manifest, resolver, local registry, workspace,
nulacommands) - Cloud Platform design document (global deploy, auto-scaling, persistence)
| Phase | Feature | Status |
|---|---|---|
| v0.2 | Hindley-Milner type checker + effect checker | Completed |
| v0.3 | Actor scheduler + supervision trees | Completed |
| v0.4 | ORCA garbage collector | Completed |
| v0.5 | Multi-node distribution | Completed |
| v0.6 | CRDT integration | Completed |
| v0.7 | BEAM/OTP primitives (monitor, links, exit, trap_exit, registry, timers, process groups, selective receive with after, spawn link/spawn monitor, actor priority — perform Actor.* language surface) |
Completed |
| v0.8 | Performance improvements (mimalloc, lock-free mailboxes, linear type moves) | Completed |
| v0.9 | Cranelift JIT backend | Completed |
| v0.10 | Type guard stripping + LSP inlay hints | Completed |
| v0.11 | SIMD vectorization (auto-vectorize array loops) | Completed |
| v0.13 | Python interop (Native Actor) + stress tests + AI ecosystem design foundation | Completed |
| v0.14 | Native AOT backend + WASM compilation and execution backends + Package Manager | Completed |
| — | Durable workflow runtime (steps, timers, signals, saga compensation) | Completed |
| — | AI runtime (agent keyword, LLM providers, memory, pipeline/debate/supervisor patterns) |
Completed |
| v1.0 | Production release — requires: chaos test suite passing ✅, scheduler profiled ✅, cycle detector intra-node only ✅ | Planned |
receiveuses selective matching without payload patterns.receive { | Behavior(params) => expr }scans the mailbox in FIFO order for the first message matching any arm (src/mir_lower.rslower_receive→OpCode::ReceiveMatch→ActorVmCallbacks::try_receive_match), binds payload values to the arm's params (missing values bind to nil, extras ignored), and skips non-matching messages, which stay queued. In the plain form it never blocks: when nothing matches, a legacy fallback pops the next message and yields its first payload value (nil when the mailbox is empty or outside an actor context). The timed formreceive { arms } after ms => body(OpCode::ReceiveWait0xA0) does block — the actor suspends until a matching message arrives or the timeout fires, then runs the after body. Payload matching is by behavior name and arity-free — no guard expressions or payload patterns yet.- Actor messaging that is fully wired goes through named behavior dispatch (
send actor behavior(args)) — seeexamples/counter_actor.nula.
- SPEC2.md — Language specification covering syntax, semantics, type system, runtime, and standard library
- ARCHITECTURE.md — Implementation architecture notes
- docs/archive/ — Historical specs, roadmaps, design documents, and review reports
Nulang is open source and licensed under the Apache License, Version 2.0. See the LICENSE file for the full license text.
Copyright 2026 © David Porkka
- Erlang/OTP for the actor model and fault-tolerance philosophy
- Pony for the capability system and ORCA GC design
- Koka and Eff for algebraic effects
- Rust for ownership-based memory safety inspiration
- Shapiro et al. for CRDT theory and the state-based replication model
"Concurrency should be a language primitive, not a library afterthought."