Tutorial
Welcome to Nulang! This tutorial walks you through the language from first principles. By the end, you’ll be writing actors, handling effects, and building distributed systems.
1. Installation
Section titled “1. Installation”Install Nulang by building from source:
git clone https://github.com/nulang-org/nulang.gitcd nulangcargo build --releaseVerify your installation:
cargo run -- --versioncargo run -- --replFor detailed platform-specific instructions, see the Installation page.
2. Hello, World!
Section titled “2. Hello, World!”Nulang uses algebraic effects for I/O. Print to the console with perform IO.print:
perform IO.print("Hello, Nulang!")Run it:
cargo run -- --eval 'perform IO.print("Hello, Nulang!")'Or start the REPL and type interactively:
cargo run -- --replnulang> perform IO.print("Hello, Nulang!")3. Variables and Functions
Section titled “3. Variables and Functions”Bind values with let and define functions with fn:
let x = 42let greeting = "Hello"
fn add(a: Int, b: Int) -> Int { a + b}
perform IO.print(perform Int.to_string(add(x, 8)))Closures capture their environment:
let base = 10let add_base = fn(x) { x + base }perform IO.print(perform Int.to_string(add_base(5))) // 154. Types and Pattern Matching
Section titled “4. Types and Pattern Matching”Nulang has Hindley-Milner type inference — types are usually optional. Declare custom variant types and match on them:
type Option[T] = Some(T) | None
fn unwrap_or(opt: Option[Int], default: Int) -> Int { match opt with { | Some(x) => x | None => default }}
let present = Some(42)let absent = Noneperform IO.print(perform Int.to_string(unwrap_or(present, 0))) // 42perform IO.print(perform Int.to_string(unwrap_or(absent, 99))) // 99Pattern matching supports guards, nested patterns, records, and wildcards:
type Color = Red | Green | Blue
fn describe(c: Color) -> String { match c with { | Red => "warm" | Green => "natural" | Blue => "cool" }}
fn classify(n: Int) -> String { match n with { | x if x < 0 => "negative" | 0 => "zero" | _ => "positive" }}5. Actors
Section titled “5. Actors”Actors are the core concurrency primitive. Each actor has private state and a set of named behaviors:
actor Counter { state count: Int = 0
behavior inc() { self.count = self.count + 1 }
behavior get() { self.count }}Spawn an actor, call behaviors with ask, or fire-and-forget with send:
actor Counter { state count: Int = 0 behavior inc() { self.count = self.count + 1 } behavior get() { self.count }}let counter = spawn Counter {}let result = ask counter get() // 0ask counter inc()ask counter inc()let result2 = ask counter get()perform IO.print(perform Int.to_string(result2)) // 2Actors can link together for fault tolerance, register names in the global registry, and monitor each other’s lifecycle:
actor Worker { behavior start(name: String) { perform Actor.register(name) perform IO.print(name + " registered") } behavior work() { perform Actor.exit(0) // normal shutdown }}See Distributed Actors for distribution, clustering, and supervision trees.
6. Algebraic Effects
Section titled “6. Algebraic Effects”Effects are Nulang’s mechanism for side-effect management. The compiler tracks which effects each function may perform, and handlers intercept those effects at runtime:
effect Logger { log: (String) -> Unit}
fn greet(name: String) { perform Logger.log("greeting " + name) perform IO.print("Hello, " + name)}
handle greet("World") { | Logger.log(msg) resume => { perform IO.print("[LOG] " + msg) }}Built-in effects include IO, Timer, Signal, Inference (LLM), Actor, and Otp (supervisors). See the Standard Library for the complete reference.
7. Next Steps
Section titled “7. Next Steps”You’ve covered the foundations. Here’s where to go next:
- Language Syntax — deep dive into expressions, declarations, and modules
- Type System — reference capabilities, row polymorphism, and effect rows
- Distributed Actors — clustering, location transparency, and CRDTs
- AI Agents — LLM integration, pipelines, debates, and supervisor teams
- Durable Workflows — signals, timers, queries, and saga transactions
Browse the full API Reference or explore the examples on GitHub.