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.

Functions
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.

Variables
let x = 42;          // immutablevar counter = 0;     // mutablecounter = counter + 1;  // OK// x = 10;            // compile error: x is immutable

Control flow

Hew provides if/else, match, while, and for expressions. match must be exhaustive — the compiler verifies all cases are covered.

if / else
let status = if count > 0 {    "active"} else {    "idle"};
match
match direction {    North => move_up(),    South => move_down(),    East => move_right(),    West => move_left(),}
Loops
// 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.

Actors
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-based tasks
scope {    let worker = spawn (x: i32) => {        process(x);    };    worker <- 42;   // send to lambda actor with <- operator}  // worker is terminated when scope exits

Named 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.

Direct method calls vs <- operator
// 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.

select with timeout
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.

Supervisors
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 types
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;}