Std.Otp
View source →Public names for the sealed raw carriers used by this module's signatures.
These aliases make the Std.Otp interface self-contained without leaking
every bare name imported from Std.Otp.Raw into downstream modules.
Types
-
type Plain -
type Server -
type RawPid -
type RawServerPid -
type RawDepServerPid -
type RawFsmPid -
type RawSupervisorPid -
type NoMessage -
type BarePid -
type Ref -
type MonitorRef -
type TimerRef -
type RawTerm -
type Selector -
type PidThe typed BEAM process algebra — the sanctioned concurrency surface.
Two typed handles, both opaque phantoms erased to a raw
erlang:pid()at runtime; their whole job is static — using a process at the WRONG message or reply type is a COMPILE error, the guaranteeStd.Otp.Raw's untypedPidcannot give (design 2026-07-10-checked-beam-concurrency §2, 2026-07-09-typed-beam-process-algebra):Pid(m)— a plain process accepting messages of typem(rawerlang:send, one-way).GenServer(q, r)— agen_servertaking requestsq; a synchronouscallreturns a replyr. The reply type rides on the SERVER'S type, socallneeds no return-only implicit (which does not resolve through an effectful extern) — a wrong request AND a wrong reply are both caught.
Every operation performs a BEAM side effect, so it returns
Effect(T). The effect discipline forbids duplicating, dropping, or reordering an OPERATION — a statement about the PROGRAM'S EFFECT SEQUENCE, not about the mailbox. The BEAM promises no delivery at all (atellto a dead process silently succeeds) and orders signals only PAIRWISE, from one sender to one target; nothing in this module claims more. Messages are POLYMORPHIC (the narrow point for a message set), neverAny.The typed layer calls the raw externs through ordinary qualified Cure definitions. The only foreign declarations are in
Std.Otp.Raw; the imported opaque carrier preserves erased runtime identity while the public aliases below attach message and request/reply indices. A typed handle to a plain BEAM process accepting messages of typem. It answers raw sends, and nothing else. -
type GenServerA typed
gen_servertaking requestsq, replyingr. DISTINCT fromPid(m):call/cast/stopare gen_server protocol operations, and a plain spawned process cannot answer them — it would block the caller for 5s and then exit it. ThePlain/Servertag is a phantom at kindType: no values, no runtime cost. -
type ActorServerAn actor-like server has independent asynchronous and synchronous input codes. All three indices are erased; the runtime value is the native pid.
-
type FsmPidTyped handle for a generated finite-state machine.
stateanddataare phantom observations;eventis the only asynchronously accepted code. -
type SupervisorHandleTyped handle to an OTP supervisor. The carrier is a bare native pid, but it is not interchangeable with an actor, FSM, or freely sendable process.
-
type StartResult = Started | StartFailed | StartIgnored | InvalidStartResult -
type DepActorServer -
type ReplyThe callback-side one-shot reply capability supplied by
gen_server. It erases to OTP's{pid, tag}tuple but remains linear at Cure call sites. -
type SubjectA typed message ADDRESS — the Gleam-style
Subject. A fresh(owner-pid, unique-tag)pair; UNLIKEPid(m)(one message type per process) a single process may own SEVERAL subjects of different types, each a distinct typed channel.subject_senddelivers a TAGGED message to the owner;subject_receivematches that tag, so channels never cross. Erases to the bare{pid, ref}tuple — no registry /persistent_term(AtomVM-safe). This is the addressing layerSelector(typed selective receive) and typedNamebuild on. -
type MessageCode = Empty | ShapeA process message set. The first algebra floor stores tag and arity shapes; typed ADT payload derivation is added by the transparent macro phase once callback syntax is available.
-
type BeamTermTyped lifecycle and observation wrappers over the raw effect boundary.
MonitorRefandTimerRefare the raw base's own DISTINCT carriers (imported, not aliased): they used to be two names for oneRef, which madecancel_timer(monitor_ref)well-typed.
Functions
-
# fn <-|(m: Type, r: Type, k: Type, dest: RawPid(m, r, k), msg: m) -> Effect(Unit)
Melquiades is ordinary operator sugar over the checked operation above. Both spellings have exactly
tell's indexed message check and effect result; neither restores the retired raw-process send node. -
# fn actor_call(m: Type, q: Type, r: Type, server: ActorServer(m, q, r), request: q) -> Effect(r)
-
# fn actor_call_dep(m: Type, q: Type, rep: Function(q, Type), server: DepActorServer(m, q, rep), request: q) -> Effect(rep(request))
-
# fn actor_cast(m: Type, q: Type, r: Type, server: ActorServer(m, q, r), message: m) -> Effect(Unit)
-
# fn actor_stop(m: Type, q: Type, r: Type, server: ActorServer(m, q, r)) -> Effect(Unit)
-
# fn as_dep(q: Type, rep: Function(q, Type), server: GenServer(q, Unit)) -> DepGenServer(q, rep)
View a running
gen_serveras a dependent server under a chosen reply familyrep(from the expected result type). Zero runtime content beyond the wrapped pid. -
# fn as_dep_actor(m: Type, q: Type, rep: Function(q, Type), server: ActorServer(m, q, Unit)) -> DepActorServer(m, q, rep)
-
# fn call(q: Type, r: Type, server: GenServer(q, r), request: q) -> Effect(r)
Synchronous
gen_servercall: send requestq, block for replyr. Both the request AND the reply are checked against the server's type.⚠ PARTIAL. On timeout (default 5000 ms) or server death the CALLER EXITS.
Effect(r)is sound — no value is ever returned at the wrong type — but it is NOT total: there is simply no continuation. Atry_callreifying the failure as a value needs a try/catch shim and is deferred (audit §6). -
# fn call_dep(q: Type, rep: Function(q, Type), server: DepGenServer(q, rep), request: q) -> Effect(rep(request))
-
# fn cancel_timer(ref: TimerRef) -> Effect(Option(Int))
Cancel a timer, surfacing what the BIF actually reports:
Some(ms)when the timer was still pending (msmilliseconds were left on it),Nonewhen it had already fired or been cancelled. The oldEffect(Unit)threw that answer away, which is how a caller could believe a cancellation succeeded when it had not.⚠ AtomVM:
erlang:send_after/3registers its timer under a DIFFERENT ref than the one it returns (timer_manager.erl:87-91), so on AtomVM cancelling asend_afterref always yieldsNoneAND THE MESSAGE STILL FIRES. Cancellation is reliable on OTP; on AtomVM it is not. See the audit, §5. -
# fn cast(q: Type, r: Type, server: GenServer(q, r), request: q) -> Effect(Unit)
Asynchronous
gen_servercast — a typed fire-and-forget request (no reply).gen_server:cast/2answers the constant atomok; the wrapper discards it. -
# fn demonitor(ref: MonitorRef) -> Effect(Unit)
Remove a monitor AND flush any
DOWNalready sitting in the mailbox. Withoutflusha staleDOWNoutlives the call, and every receivingmatchhas to carry an arm for a monitor the caller believes it already removed. -
# fn dep_actor_cast(m: Type, q: Type, rep: Function(q, Type), server: DepActorServer(m, q, rep), message: m) -> Effect(Unit)
-
# fn dep_actor_stop(m: Type, q: Type, rep: Function(q, Type), server: DepActorServer(m, q, rep)) -> Effect(Unit)
-
# fn exit(m: Type, r: Type, k: Type, pid: RawPid(m, r, k), reason: ExitReason) -> Effect(Unit)
Send an exit signal. Makes NO type-level claim that the target dies — correctly: which of the three outcomes occurs depends on the target's trap_exit flag, which the sender cannot see. A polymorphic reason could not even state the distinction.
-
# fn fsm_send(event: Type, state: Type, data: Type, machine: FsmPid(event, state, data), value: event) -> Effect(Unit)
-
# fn handles(code: MessageCode, tag: Atom, arity: Int) -> Bool
Does
codeaccept a message tag with the given payload arity? -
# fn is_alive(m: Type, r: Type, k: Type, pid: RawPid(m, r, k)) -> Effect(Bool)
-
# fn link(m: Type, r: Type, k: Type, pid: RawPid(m, r, k)) -> Effect(Unit)
erlang:link/1,unlink/1andregister/2all answer the constanttrue— there is no failure they report by value (they raise instead). The wrappers discard it. -
# fn map_start(a: Type, b: Type, result: StartResult(a), convert: Function(a, b)) -> StartResult(b)
-
# fn message_code(tag: Atom, arity: Int) -> MessageCode
A single message shape constructor, convenient for macro-generated codes.
-
# fn monitor(m: Type, r: Type, k: Type, kind: MonitorKind, pid: RawPid(m, r, k)) -> Effect(MonitorRef)
-
# fn name(m: Type, registered: Atom) -> Name(m)
A typed name over a registered atom.
masserts what the named process accepts. -
# fn new_selector(p: Type) -> Selector(p)
Typed SELECTIVE RECEIVE (Gleam-style
Selector). Accumulate handlers over several subjects of DIFFERENT message types — each mapped into a common payloadpbyselect_map— thenselector_receivereturns whichever arrives first, typed asp.selectis the identity case (the subject's messages ARE the payload). Building a selector is pure; only the receive is anEffect. -
# fn new_subject(m: Type) -> Effect(Subject(m))
Mint a fresh subject owned by the calling process.
-
# fn register(m: Type, r: Type, k: Type, name: Atom, pid: RawPid(m, r, k)) -> Effect(Unit)
-
# fn register_name(m: Type, n: Name(m), pid: Pid(m)) -> Effect(Bool)
Register a
Pid(m)under the name. Returns whether registration succeeded (false if the name is taken). -
# fn reply(r: Type, from: Reply(r), value: r) -> Effect(Unit)
Send one reply through a callback's linear
fromcapability. Generated actor callbacks use this direct operation and returnnoreply. -
# fn select(p: Type, selector: Selector(p), subject: Subject(p)) -> Selector(p)
Add a subject whose message type IS the payload (identity mapping).
-
# fn select_map(p: Type, m: Type, selector: Selector(p), subject: Subject(m), transform: Function(m, p)) -> Selector(p)
Add
subject, mapping its messages into the selector's payloadp. -
# fn selector_receive(p: Type, selector: Selector(p), timeout: Int) -> Effect(Option(p))
Receive from whichever selected subject arrives first within
timeoutms;Noneon timeout. -
# fn self(m: Type) -> Effect(Pid(m))
The calling process's own typed pid; the erased message index is supplied by the enclosing expected result at each use site.
-
# fn send_after(m: Type, r: Type, k: Type, delay: Int, pid: RawPid(m, r, k), msg: m) -> Effect(TimerRef)
The signal-layer operations below accept BOTH handles: linking, monitoring, exiting, naming and timing are properties of a BEAM PROCESS, not of the protocol it speaks. They are tag-polymorphic in
k. -
# fn spawn(m: Type, thunk: Function(Unit)) -> Effect(Pid(m))
Spawn a nullary process function and return its indexed handle.
-
# fn spawn_link(m: Type, thunk: Function(Unit)) -> Effect(Pid(m))
Spawn and link a nullary process function to the calling process.
-
# fn start_actor(a: Type, m: Type, q: Type, r: Type, module: Atom, args: a) -> Effect(StartResult(ActorServer(m, q, r)))
Start a checked actor callback module and decode OTP's heterogeneous result. Only the
:okbranch may attach actor protocol indices, and only after the second tuple element has passed the native PID guard. -
# fn start_fsm(a: Type, event: Type, state: Type, data: Type, module: Atom, args: a) -> Effect(StartResult(FsmPid(event, state, data)))
-
# fn start_link(a: Type, module: Atom, args: a) -> Effect(Tuple)
Start a gen_server callback module. The generic floor preserves OTP's result tuple; transparent behavior macros provide the typed specialization.
-
# fn start_statem(a: Type, module: Atom, args: a) -> Effect(Tuple)
-
# fn start_supervisor(module: Atom) -> Effect(Tuple)
-
# fn start_supervisor_with(a: Type, module: Atom, args: List(a)) -> Effect(Tuple)
-
# fn start_typed_supervisor(module: Atom) -> Effect(StartResult(SupervisorHandle))
-
# fn stop(q: Type, r: Type, server: GenServer(q, r)) -> Effect(Unit)
Stop a
gen_server.Server-only —gen_server:stopon a plain process blocks and then exits the caller. The BIF answers the constant atomok; discarded. -
# fn stop_supervisor(supervisor: SupervisorHandle, reason: ExitReason) -> Effect(Unit)
-
# fn subject_receive(m: Type, subject: Subject(m), timeout: Int) -> Effect(Option(m))
Receive the next message addressed to
subjectwithintimeoutms;Noneon timeout. Only the subject's OWNER (the process that created it) may receive. -
# fn subject_send(m: Type, subject: Subject(m), message: m) -> Effect(Unit)
Send a typed message to a subject — delivered TAGGED to the owner's mailbox (fire-and-forget, like
tell). -
# fn subset(narrow: MessageCode, wide: MessageCode) -> Bool
Is every shape in
narrowalso accepted bywide? -
# fn tell(m: Type, r: Type, k: Type, dest: RawPid(m, r, k), msg: m) -> Effect(Unit)
Send a well-typed message:
msg : mmust matchdest's accepted typem, so aPid(Command)can never be sent aTelemetry. (Namedtell, notsend: the Melquiades operators below provide the compact send surface.)Accepts BOTH handles: a raw send to a gen_server is legitimate BEAM practice — it lands in
handle_info. The raw BIF returns the message itself; the typed wrapper DISCARDS it — a send is fire-and-forget, and echoing the argument back tells the caller nothing. Discarding is a choice the typed layer makes explicitly, not a lie the raw type tells. -
# fn union(left: MessageCode, right: MessageCode) -> MessageCode
Union two codes, preserving the first code's order and removing duplicate tag/arity shapes from the second code.
-
# fn unlink(m: Type, r: Type, k: Type, pid: RawPid(m, r, k)) -> Effect(Unit)
-
# fn unregister(name: Atom) -> Effect(Unit)
-
# fn unregister_name(m: Type, n: Name(m)) -> Effect(Bool)
Remove the name's registration. Returns whether it was registered.
-
# fn whereis(name: Atom) -> Effect(Option(BarePid))
Look up a registered name.
Nonewhen the name is not registered — the BIF returns the atomundefinedthere, and the old signature asserted a pid.The result is a
BarePid: you maylink,monitor,exitoris_aliveit, but you cannottellto it. Nothing associates a registered NAME with a message TYPE, so a typed handle cannot be recovered from one (audit F-1 / parent spec §13.7). -
# fn whereis_name(m: Type, n: Name(m)) -> Effect(Option(Pid(m)))
Look up the process registered under the name as a TYPED, sendable
Pid(m);Noneif unregistered. -
# fn ✉(m: Type, r: Type, k: Type, dest: RawPid(m, r, k), msg: m) -> Effect(Unit)