언어 기능
Hew는 복원력 있고 고성능인 서비스를 위해 만들어졌습니다. 이 페이지는 오늘의 v0.9.3 기능 세트와 더 넓은 언어 방향을 구분합니다.
액터 모델
채팅방 예제에서는 한 액터가 멤버 수와 메시지 수를 소유합니다. 그 상태는 receive 메서드만 변경하며, 각 호출은 타입이 지정된 결과를 반환합니다.
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
}
}메모리 모델
일반 소유 값은 재귀적으로 해제됩니다. 아핀 리소스는 하나의 정리 책임만 유지합니다. #[resource]는 스코프 종료 시 안전하게 정리하고, #[linear]는 모든 경로에서 명시적인 소비 작업을 요구합니다.
감독 트리
예제는 one_for_one 감독자 아래에 세 자식을 선언하고, 분당 다섯 번의 재시작 예산과 자식별 permanent 또는 transient 정책을 설정합니다.
// 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;
}구조적 동시성
배치는 fork로 세 독립 작업을 시작하고 await로 결과를 모읍니다. scope는 배치가 반환되기 전에 자식 작업의 완료를 기다립니다.
// 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
}동시성 패턴
예제는 select와 after timeout으로 두 액터 호출을 경쟁시키고, 이어서 await fork (...)로 두 독립 호출을 시작해 양쪽 응답을 모읍니다.
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}");
}상태 머신
연결 머신은 이벤트, 상태, 가드가 있는 전이, 기본 동작을 한 표에 선언합니다. step은 현재 상태와 이벤트에 선언된 전이만 적용합니다.
// 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
}실패를 타입으로 다루는 액터 호출
이 로컬 액터 예제는 각 요청이 타입화된 결과를 반환하고 호출자가 처리하거나 전파하는 호출 경계를 보여 줍니다.
// 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"),
}
}와이어 계약
프로필 스키마는 각 필드에 안정적인 숫자 태그를 부여합니다. 선택적인 verified 필드는 이전 또는 이후 버전의 상대가 생략할 수 있고, 호환성 검사기는 태그 변경을 추적합니다.
// 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;
}타입 시스템
Hew의 타입 시스템은 액터 격리와 capability로 검사되는 경계를 중심으로 만들어졌습니다. 컴파일러는 컴파일 시점에 Send/Frozen 검사, 완전성, 액터 격리를 강제합니다.
모노모피제이션을 사용하는 제네릭
컴파일러는 프로그램이 사용하는 각 구체 타입에 맞춰 제네릭 타입과 함수를 특수화합니다.
Send + Frozen capability 트레이트
Send는 액터 경계를 넘을 수 있는 값을 표시합니다. Frozen은 안전하게 공유할 수 있는 깊은 불변 값을 표시합니다.
양방향 타입 추론
예상 타입이 람다와 제네릭 호출로 흐르므로 주석을 줄이면서 추론된 타입을 검사기가 확인할 수 있습니다.
완전성 검사가 있는 패턴 매칭
match 표현식은 컴파일 타임에 완전성을 검사받습니다. 컴파일러는 가능한 모든 경우가 처리되었는지 보장합니다.
extern blocks를 통한 C FFI
extern blocks는 C 심볼과 Hew 시그니처를 설명합니다. FFI는 기본적으로 unsafe이며 ABI, 포인터, 수명의 정확성은 호출자 책임입니다.
Option<T> 및 Result<T, E>
부재와 복구 가능한 실패는 명시적인 값이며, 패턴 매칭으로 처리하거나 ?로 전파합니다.
어댑터가 있는 Iterator trait
map, filter, fold 같은 Iterator 어댑터는 시퀀스 위에서 지연 변환을 조합합니다.
컴파일
Hew는 오늘 네이티브 머신 코드로 컴파일됩니다. hew build 명령은 Rust 프런트엔드와 Rust LLVM 백엔드를 구동하며, 각 단계는 더 많은 검사기 보장을 보존합니다.