GenServer
OTP is a standard-library surface, not special compiler syntax. A server is an
ordinary module implementing gale_std.gen_server.Server. Gale emits the native
callback module with @behaviour GenServer and explicit @impl GenServer
attributes; it does not emit use GenServer.
module genserver
implements gale_std.gen_server.Server< Integer, Integer, CounterCall, CounterCast, Never, Never> as counter
pub type CounterCall = | Value(reply_to: gale_std.gen_server.ReplyTo<Integer>) | Add(amount: Integer, reply_to: gale_std.gen_server.ReplyTo<Integer>)
pub type CounterCast = Reset | Incrementpub type CounterTarget = gale_std.gen_server.ServerTarget<counter>
pub fn start_link(initial: Integer) -> gale_std.gen_server.StartResult<counter> { gale_std.gen_server.start_link(genserver, initial, [])}
pub fn child_spec(initial: Integer) -> ChildSpec<counter> { gale_std.gen_server.child_spec(genserver, initial)}
pub fn value(server: CounterTarget) -> Integer { gale_std.gen_server.call(server, fn(reply_to) => Value(reply_to))}
pub fn add(server: CounterTarget, amount: Integer) -> Integer { gale_std.gen_server.call(server, fn(reply_to) => Add(amount, reply_to))}
pub fn reset(server: CounterTarget) -> :ok { gale_std.gen_server.cast(server, Reset)}
pub fn increment(server: CounterTarget) -> :ok { gale_std.gen_server.cast(server, Increment)}
pub fn init(initial: Integer) -> gale_std.otp.Init<Integer, Never> { (:ok, initial)}
pub fn handle_call<R>( build: fn(gale_std.gen_server.ReplyTo<R>) -> CounterCall, from: gale_std.gen_server.ReplyTo<R>, state: Integer) -> gale_std.otp.Next<Integer, Never> { match build(from) { Value(reply_to) -> { gale_std.gen_server.reply(reply_to, state) (:noreply, state) } Add(amount, reply_to) -> { let next = state + amount gale_std.gen_server.reply(reply_to, next) (:noreply, next) } }}
pub fn handle_cast( message: CounterCast, state: Integer) -> gale_std.otp.Next<Integer, Never> { match message { Reset -> (:noreply, 0) Increment -> (:noreply, state + 1) }}Calls return their native reply directly and exit on timeout or server
failure. The builder closure is the construction that lets Gale associate
each call constructor with the exact ReplyTo<R> it must answer. Casts and
ordinary messages remain separate native channels.
child_spec/1 is ordinary typed Gale code replacing the convenience that
use GenServer would inject. That keeps Elixir’s {GenServer, initial}
supervisor form interoperable without invoking a macro.
match genserver.start_link(10) { (:ok, server) -> { let _ = genserver.increment(server) let _ = gale_std.test.assert(genserver.value(server) == 11) let _ = gale_std.test.assert(genserver.add(server, 4) == 15) let _ = genserver.reset(server) gale_std.test.assert(genserver.value(server) == 0) } :ignore -> gale_std.test.assert(false) (:error, _) -> gale_std.test.assert(false)}