Sprachfunktionen
Hew ist für resiliente und leistungsstarke Services gebaut. Diese Seite trennt den v0.9.3-Funktionsumfang von der größeren Sprachrichtung.
Actor-Modell
Das Chatroom-Beispiel hält Mitglieder- und Nachrichtenzahl in einem Actor. Nur seine receive-Methoden verändern diesen Zustand, und jeder Aufruf liefert ein typisiertes Ergebnis.
actor ChatRoom {
var members: i64 = 0;
var message_count: i64 = 0;
receive fn join(user_id: i64) -> i64 {
members = members + 1;
members
}
receive fn post_message(content: string) -> i64 {
message_count = message_count + 1;
message_count
}
}Speichermodell
Gewöhnliche Owned Values werden rekursiv freigegeben. Bei affinen Ressourcen gibt es genau eine Cleanup-Verantwortung: #[resource] übernimmt das sichere Cleanup am Scope-Ende, während #[linear] auf jedem Pfad eine explizite konsumierende Aktion verlangt.
Supervision-Bäume
Das Beispiel deklariert drei Kinder unter einem one_for_one-Supervisor, mit einem Budget von fünf Neustarts pro Minute sowie permanenten oder transienten Richtlinien pro Kind.
// A supervisor declares its restart strategy, its restart budget
// (`intensity: N within <window>`), and its children. Each child names the
// actor type and its init args; a per-child `restart:` policy decides whether
// a crash is restarted (permanent), restarted only on failure (transient),
// or never (temporary).
actor DatabasePool {
let pool_size: i64;
receive fn query() -> i64 {
pool_size
}
}
actor CacheLayer {
let capacity: i64;
receive fn get() -> i64 {
capacity
}
}
actor ApiHandler {
let port: i64;
receive fn handle() {}
}
supervisor ServiceCluster {
strategy: one_for_one;
intensity: 5 within 60s;
child db: DatabasePool { pool_size: 16 } restart: permanent;
child cache: CacheLayer { capacity: 1024 } restart: permanent;
child api: ApiHandler { port: 8080 } restart: transient;
}Strukturierte Nebenläufigkeit
Der Batch startet drei unabhängige Aufgaben mit fork und sammelt ihre Ergebnisse mit await. Der scope wartet auf die Kindarbeit, bevor der Batch zurückkehrt.
// Structured concurrency: a `scope` block opens a region that joins all
// child work before it returns. Start independent jobs with `fork`, then
// collect each result with `await`.
fn process_batch(a: i64, b: i64, c: i64) -> i64 {
var total = 0;
scope {
let ra = fork run_job(a);
let rb = fork run_job(b);
let rc = fork run_job(c);
let first = await ra;
let second = await rb;
let third = await rc;
total = first + second + third;
};
total
}
fn run_job(job: i64) -> i64 {
job * 2
}Nebenläufigkeitsmuster
Das Beispiel lässt zwei Actor-Aufrufe mit select und einem after-Timeout gegeneinander laufen. Anschließend startet await fork (...) zwei unabhängige Aufrufe und sammelt beide Antworten.
actor Fetcher {
receive fn fetch(key: i64) -> i64 {
key
}
}
actor Store {
receive fn get_users() -> i64 {
0
}
receive fn get_posts() -> i64 {
0
}
}
// Wait for the first responder, with a timeout fallback. Each arm races an
// actor ask; the first to reply wins and the rest are cancelled.
fn race_fetch(primary: Fetcher, fallback: Fetcher, key: i64) -> i64 {
select {
resp from primary.fetch(key) => resp handle e { -1 },
resp from fallback.fetch(key) => resp handle e { -1 },
after 500ms => -1,
}
}
// Run independent calls concurrently and collect every result.
fn parallel_fetch(db: Store) -> (i64, i64) {
let (users, posts) = await fork (db.get_users(), db.get_posts());
(users.expect("store stopped"), posts.expect("store stopped"))
}
fn main() {
let primary = spawn Fetcher;
let fallback = spawn Fetcher;
println(race_fetch(primary, fallback, 42));
let db = spawn Store;
let (users, posts) = parallel_fetch(db);
println(f"{users} {posts}");
}Zustandsautomaten
Der Verbindungsautomat deklariert Events, Zustände, bewachte Übergänge und Standardverhalten in einer Tabelle. step wendet nur einen Übergang an, der für den aktuellen Zustand und das aktuelle Event deklariert ist.
// A connection lifecycle as a compiler-checked state machine.
// Events are declared up front; transitions route with `=>` and a body
// that constructs the target state. Every (state, event) pair must be
// covered — `default { state }` makes the rest a no-op stay.
machine Connection {
events {
Connect { host: string; }
Disconnect;
HeartbeatTimeout;
}
state Idle;
state Connecting { host: string; }
state Connected { host: string; }
state Disconnected { retries: i64; }
on Connect { host }: Idle => Connecting { host: host }
on Connect { host }: Disconnected => Connecting when state.retries < 5 { host: host }
on Disconnect: Connected => Disconnected { retries: 0 }
on HeartbeatTimeout: Connected => Disconnected { retries: 0 }
default { state }
}
fn main() {
var conn = Connection.Idle;
_ = conn.step(Connection.Event.Connect { host: "10.0.0.1" });
println(conn.state_name()); // Connecting
}Fehlerbewusste Actor-Aufrufe
Dieses Beispiel mit einem lokalen Actor zeigt die fehlerbewusste Aufrufgrenze: Jede Anfrage liefert ein typisiertes Ergebnis, das der Aufrufer behandelt oder weitergibt.
// Actor calls make failure part of the method surface. Every call yields a
// Result, so callers handle a stopped actor or expired request deadline alongside
// the successful value. The error branch below has the typed
// ActorError<Never, Never> carried by this method.
actor Worker {
let id: i64;
receive fn process(task: i64) -> i64 {
task + id
}
}
fn dispatch(task: i64) -> i64 fails ActorError<Never, Never> {
let w = spawn Worker { id: 1 };
// `?` on the binding unwraps Ok and propagates a typed ActorError<Never, Never>
// on the failure branch.
let answer = (w.process(task))?;
answer
}
fn main() {
match dispatch(7) {
.Ok(v) => println(f"result = {v}"),
.Err(_) => println("dispatch failed"),
}
}Wire-Verträge
Das Profilschema gibt jedem Feld eine stabile numerische Kennung. Das optionale Feld verified kann von älteren oder neueren Gegenstellen ausgelassen werden, während die Kompatibilitätsprüfung Änderungen der Kennungen verfolgt.
// Wire types carry explicit field tags (@1, @2, ...) for forward- and
// backward-compatible binary encoding. Optional fields may be omitted by
// older or newer peers without breaking the schema.
#[wire]
type UserProfile {
name: string @1;
email: string @2;
age: i32 @3;
verified: Option<bool> @4 optional;
}Typsystem
Das Typsystem von Hew ist um Actor-Isolation und capability-geprüfte Grenzen herum aufgebaut. Der Compiler erzwingt Send/Frozen-Prüfungen, Vollständigkeit und Actor-Isolation zur Kompilierzeit.
Generics mit Monomorphisierung
Der Compiler spezialisiert generische Typen und Funktionen für jeden konkreten Typ, den das Programm verwendet.
Send- und Frozen-Capability-Traits
Send kennzeichnet Werte, die eine Actor-Grenze überschreiten dürfen. Frozen kennzeichnet tief unveränderliche Werte, die sicher gemeinsam genutzt werden können.
Bidirektionale Typinferenz
Erwartete Typen fließen in Lambdas und generische Aufrufe. Das reduziert Annotationen, während der abgeleitete Typ für den Prüfer sichtbar bleibt.
Pattern Matching mit Vollständigkeit
Match-Ausdrücke werden zur Kompilierzeit auf Vollständigkeit geprüft. Der Compiler stellt sicher, dass jeder mögliche Fall behandelt wird.
C FFI mit extern-Blöcken
Extern-Blöcke beschreiben C-Symbole und ihre Hew-Signaturen. FFI ist standardmäßig unsafe; ABI-, Zeiger- und Lebenszeitkorrektheit bleiben Verantwortung des Aufrufers.
Option<T> und Result<T, E>
Abwesenheit und behebbare Fehler sind explizite Werte, die per Pattern Matching behandelt oder mit ? weitergegeben werden.
Iterator-Trait mit Adaptern
Iterator-Adapter wie map, filter und fold komponieren verzögerte Transformationen über Sequenzen.
Kompilierung
Hew kompiliert heute zu nativem Maschinencode. Der Befehl hew build steuert das Rust-Frontend und das Rust-LLVM-Backend, wobei jede Stufe mehr Checker-Garantien bewahrt.