跳到主要内容

语言特性

Hew 为弹性、高性能服务而构建。本页区分当前 v0.9.3 的特性集和更广阔的语言方向。

Actor 模型

聊天室示例由一个 actor 保存成员数和消息数。只有它的 receive 方法能修改这些状态,每次调用都返回带类型的结果。

chat_room.hew
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
    }
}

内存模型

普通 owned values 会被递归释放。仿射资源只保留一份清理责任:#[resource] 在离开作用域时安全清理,而 #[linear] 要求每条路径都执行显式的消费操作。

监督树

示例在一个 one_for_one supervisor 下声明三个子项,设置每分钟五次的重启预算,并为每个子项指定 permanent 或 transient 策略。

service_cluster.hew
// 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会等待子任务完成后再返回。

batch.hew
// 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 超时让两个 actor 调用竞争,随后用 await fork (...) 启动两个独立调用并收集两份回复。

patterns.hew
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}");
}

状态机

连接状态机在一张表中声明事件、状态、带 guard 的转换和默认行为。step 只应用为当前状态和事件声明的转换。

connection.hew
// 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 调用

这个本地 actor 示例展示了对失败进行类型化的调用边界:每个请求都返回由调用方处理或传播的结果。

actor_calls.hew
// 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 契约

配置文件 schema 为每个字段分配稳定的数字标签。旧或新的对端可以省略可选的 verified 字段,而兼容性检查器会跟踪标签变化。

user.hew
// 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 的类型系统围绕 actor 隔离和 capability 检查边界构建。编译器在编译时强制执行 Send/Frozen 检查、穷尽性和 actor 隔离。

通过 monomorphization 实现泛型

编译器会针对程序使用的每个具体类型,专门化泛型类型和函数。

Send + Frozen 能力 traits

Send 标记可以跨越 actor 边界的值。Frozen 标记可安全共享的深度不可变值。

双向类型推断

预期类型会流向 lambdas 和泛型调用,在减少注解的同时让推断类型对检查器可见。

带穷尽性的模式匹配

Match 表达式会在编译期检查完整性。编译器确保每一种可能情况都被处理。

使用 extern blocks 的 C FFI

Extern blocks 描述 C symbols 及其 Hew signatures。FFI 默认是 unsafe;ABI、指针和生命周期的正确性仍由调用方负责。

Option<T> 和 Result<T, E>

缺失和可恢复失败都表示为显式值,通过模式匹配处理或用 ? 传播。

带 adapters 的 Iterator trait

map、filter、fold 等 adapters 在序列上组合惰性变换。

编译

Hew 今天可以编译为原生机器码。hew build 命令驱动 Rust frontend 和 Rust LLVM backend,每个阶段都会保留更多 checker 保证。

.hew → 前端 → 类型检查 → MIR → LLVM IR → 原生二进制