Language Tour
A quick tour of Hew's syntax and features.
Functions
Functions are declared with the fn keyword. Parameters have explicit types, and the return type follows the -> arrow. The last expression in a block is the return value.
fn add(a: i32, b: i32) -> i32 { a + b}fn greet(name: String) { println_str(f"Hello, {name}!");}Variables
Use let for immutable bindings and var for mutable bindings. Bindings are immutable by default to encourage safe, predictable code.
let x = 42; // immutablevar counter = 0; // mutablecounter = counter + 1; // OK// x = 10; // compile error: x is immutableControl flow
Hew provides if/else, match, while, and for expressions. match must be exhaustive — the compiler verifies all cases are covered.
let status = if count > 0 { "active"} else { "idle"};match direction { North => move_up(), South => move_down(), East => move_right(), West => move_left(),}// while loopwhile running { process_next();}// for loop with iteratorfor item in items { println_str(item.display());}Actors
Actors are isolated state machines that communicate via message passing. They are declared with the actor keyword. Message handlers use receive fn; internal methods use plain fn.
actor Counter { var count: i32 = 0; // Message handler — callable from other actors receive fn increment() { self.count = self.count + 1; } // Request-response handler receive fn get() -> i32 { self.count } // Internal method — not accessible externally fn validate(n: i32) -> bool { n >= 0 }}Structured concurrency
Tasks within an actor are managed via scope blocks. When a scope exits, all spawned tasks within it are cancelled cooperatively. This prevents orphaned work.
scope { let worker = spawn (x: i32) => { process(x); }; worker <- 42; // send to lambda actor with <- operator} // worker is terminated when scope exitsNamed actors use direct method calls — counter.increment(10) and await counter.get_count(). Lambda actors (created with spawn) use the <- operator for message passing: worker <- value.
// Named actor: direct method callslet counter = spawn Counter {};counter.increment(10); // fire-and-forgetlet count = await counter.get_count(); // request-response// Lambda actor: <- operatorlet logger = spawn (msg: String) => { println_str(msg);};logger <- "hello"; // send message with <-Concurrency patterns
Hew provides select, race, and join as language constructs. All support after timeouts.
let result = select { resp <- primary.fetch(key) => resp, resp <- fallback.fetch(key) => resp, after 500ms => Err("timeout"),};Supervisors
Supervisors manage actor lifecycles and restart strategies. They follow Erlang/OTP patterns with strategies like one_for_one, one_for_all, and rest_for_one.
supervisor AppSupervisor { strategy: one_for_one, max_restarts: 5, window: 60s, children { child logger: Logger restart(permanent); child cache: CacheActor restart(permanent); child worker: WorkerActor restart(transient); }}Wire types
Wire types define network-serializable data with stable field tags. They enforce forward and backward compatibility — field numbers must never be reused, and deleted fields must be reserved.
wire struct UserEvent { user_id: u64 @1; name: string @2; email: string @3 optional; role: Role @4 default(Role::Member); // reserved(5, 6) — deleted fields}wire enum Role { Admin @1; Member @2; Guest @3;}