Saltar al contenido principal

Características del lenguaje

Hew está creado para servicios resilientes y de alto rendimiento. Esta página separa el conjunto actual de v0.9.3 de la dirección más amplia del lenguaje.

Modelo de actores

El ejemplo de sala de chat guarda los recuentos de miembros y mensajes en un actor. Solo sus métodos receive modifican ese estado, y cada llamada devuelve un resultado tipado.

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
    }
}

Modelo de memoria

Los valores propios ordinarios se liberan de forma recursiva. Los recursos afines mantienen una sola responsabilidad de limpieza: #[resource] proporciona limpieza segura al salir del scope, mientras que #[linear] requiere una acción de consumo explícita en cada ruta.

Árboles de supervisión

El ejemplo declara tres hijos bajo un supervisor one_for_one, con un presupuesto de cinco reinicios por minuto y políticas permanentes o transitorias para cada hijo.

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

Concurrencia estructurada

El lote inicia tres tareas independientes con fork y recoge sus resultados con await. El scope espera el trabajo hijo antes de que termine el lote.

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
}

Patrones de concurrencia

El ejemplo hace competir dos llamadas a actores con select y un timeout after; después usa await fork (...) para iniciar dos llamadas independientes y reunir ambas respuestas.

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

Máquinas de estado

La máquina de conexión declara eventos, estados, transiciones con guardas y comportamiento predeterminado en una tabla. step aplica solo una transición declarada para el estado y el evento actuales.

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
}

Llamadas a actores con fallos tipados

Este ejemplo con un actor local muestra el límite de llamada con fallos tipados: cada solicitud devuelve un resultado que el llamador maneja o propaga.

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"),
    }
}

Contratos wire

El esquema de perfil asigna una etiqueta numérica estable a cada campo. El campo opcional verified puede omitirse en pares antiguos o nuevos, mientras el verificador de compatibilidad rastrea cambios de etiquetas.

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

Sistema de tipos

El sistema de tipos de Hew se construye alrededor del aislamiento de actores y límites verificados por capacidades. El compilador aplica verificaciones Send/Frozen, exhaustividad y aislamiento de actores en tiempo de compilación.

Genéricos con monomorfización

El compilador especializa tipos y funciones genéricas para cada tipo concreto que usa el programa.

Traits de capacidad Send + Frozen

Send marca los valores que pueden cruzar el límite de un actor. Frozen marca valores profundamente inmutables que se pueden compartir con seguridad.

Inferencia de tipos bidireccional

Los tipos esperados fluyen hacia lambdas y llamadas genéricas, reduciendo anotaciones y dejando visible el tipo inferido al verificador.

Pattern matching con exhaustividad

Las expresiones match se verifican por completitud en tiempo de compilación. El compilador asegura que cada caso posible se maneje.

FFI C con bloques extern

Los bloques extern describen símbolos C y sus firmas Hew. FFI es unsafe por defecto; ABI, punteros y vidas útiles siguen siendo responsabilidad de quien llama.

Option<T> y Result<T, E>

La ausencia y los fallos recuperables son valores explícitos, tratados con pattern matching o propagados con ?.

Trait Iterator con adaptadores

Adaptadores como map, filter y fold componen transformaciones lazy sobre secuencias.

Compilación

Hew compila a código máquina nativo hoy. El comando hew build maneja un frontend Rust y un backend Rust LLVM, y cada etapa preserva más garantías del checker.

.hew → Frontend → Verificación de tipos → MIR → LLVM IR → Binario nativo