Actor
Actor — Base class for process-based objects.
Actor inherits from Object and adds process-based concurrency.
Every actor runs in its own BEAM process and communicates via
asynchronous messages. Use spawn instead of new to create actors.
Examples
Actor subclass: Counter
state: count = 0
increment => self.count := self.count + 1
count => self.count
@see Value (for immutable data without a process) @see Supervisor (for static supervision trees) @see DynamicSupervisor (for dynamic supervision trees)
Methods
- class » spawn
- class » spawnWith: initArgs
- class » new
- class » new: _initArgs
- class » spawnAs: name
- class » spawnWith: initArgs as: name
- class » named: name
- class » spawnAs: name scope: scope
- class » spawnWith: initArgs as: name scope: scope
- class » named: name scope: scope
- class » allRegistered
- class » spawnOn: node
- class » spawnAs: name on: node
- class » spawnWith: initArgs on: node
- class » spawnWith: initArgs as: name on: node
- class » named: name on: node
- class » allRegisteredOn: node
- class » supervisionPolicy
- class » isSupervisor
- class » supervisionSpec
- registerAs: name
- unregister
- unregisterName
- registeredName
- isRegistered
- withTimeout: ms
- initialize
- terminate: _reason
- delegate
- pid
- node
- isRemote
- monitor
- onExit: block
- stop
- kill
- isAlive
Class Methods
Spawn a new actor process with default state.
The compiler's static Counter spawn and self spawn call sites lower
directly to beamtalk_actor:safe_spawn/class_self_spawn (BT-3072) —
this declaration is the documented, xref-visible source of behaviour for
the dynamic dispatch path (workspace bindings, perform:), not the
method actually invoked by those fast paths.
Examples
c := Counter spawn
Spawn a new actor process with initialization arguments.
See spawn for the static-vs-dynamic dispatch note (BT-3072).
Examples
c := Counter spawnWith: #{#count => 10}
Actors cannot be created with new — always raises (BT-217).
Actors are processes with identity and lifecycle, not immutable
values; spawn starts the process, new does not make sense for
them. Every actor subclass carries a compiled new/0 that raises this
same error directly (required by the runtime's {new, Args}
instantiation protocol, which calls a class's own compiled new/0
rather than dispatching through inherited class methods) — this
declaration is the documented, xref-visible source of that behaviour.
Examples
Counter new // => raises instantiation_error: "Use spawn instead"
Actors cannot be created with new: — always raises (BT-217).
Use spawnWith: to create an actor instance with initialization
arguments. See new for why actors reject direct instantiation.
Examples
Counter new: #{#count => 10} // => raises instantiation_error: "Use spawnWith: instead"
Atomically spawn a new actor and register it under name.
Uses gen_server:start_link({local, Name}, ...) under the hood so the
name is established during process startup — no TOCTOU window between
spawn and registerAs:. Prefer this over post-spawn registerAs:
whenever the name is known up front.
Returns Result ok: actor on success, or Result error: with one of:
name_registered— another process is already registered undernamereserved_name—nameis in the OTP kernel / stdlib blocklisttype_error—nameis not a Symbol
Examples
c := (Counter spawnAs: #counter) unwrap
(Counter spawnAs: #counter) onError: [:e | Logger warn: "name taken"]
Atomically spawn a new actor with init args and register it under name.
Same contract as spawnAs: plus the initialisation arguments passed to
the actor's init/1 callback.
Examples
c := (Counter spawnWith: #{#count => 10} as: #counter) unwrap
Look up a registered actor by name, checked against the receiver class.
Returns Result ok: actor when a process is registered under name and
its class is the receiver (or any subclass). Self resolves to the
receiver class at the call site, so subclasses inherit a typed lookup:
Counter named: #counter // => Result(Counter, Error)
Actor named: #anything // => Result(Actor, Error)
Error cases:
name_not_registered— nothing registered undernamewrong_class— registered, but not a (subclass of) the receiver classtype_error—nameis not a Symbol
Examples
engine := (WorkflowEngine named: #workflowEngine) unwrap
(Logger named: #counter) // => Result error: (beamtalk_error wrong_class)
Atomically spawn a new actor and register it under name, in the
requested cluster scope.
scope: #local behaves identically to spawnAs: — genuinely atomic:
a losing race never runs initialize at all (OTP reserves the local
name as part of the process start itself). scope: #global registers
with OTP's global module instead — the name is unique across the
whole connected cluster and resolves the same way from every node,
via global:whereis_name/1 on each send rather than a per-node
lookup — but is NOT atomic in the same way: global has no
custom-resolver variant of an atomic "start-and-register", so the
actor is started (and initialize runs to completion) before the
name is registered. If that registration then loses the race, the
already-initialized actor is torn down and name_registered is
returned — but any side effect initialize already performed (I/O,
messaging another actor, etc.) already happened, even though the
caller sees the same clean failure #local would give without ever
running initialize. Idempotent initialize overrides are
unaffected; one with externally-visible side effects should account
for this under scope: #global.
Returns Result ok: actor on success, or Result error: with one of:
name_registered— another process is already registered undernamein that scopereserved_name—nameis in the OTP kernel / stdlib blocklisttype_error—nameis not a Symbol, orscopeis neither#localnor#global
Examples
leader := (Scheduler spawnAs: #scheduler scope: #global) unwrap
// on any node in the cluster:
s := (Scheduler named: #scheduler scope: #global) unwrap
Atomically spawn a new actor with init args and register it under
name, in the requested cluster scope.
Same contract as spawnAs:scope: plus the initialisation arguments
passed to the actor's init/1 callback. initArgs goes through the
same wire encoder as every other cross-node argument (ADR 0126 §5.1)
— no second encode path.
Examples
c := (Counter spawnWith: #{#count => 10} as: #counter scope: #global) unwrap
Look up an actor by name in the requested cluster scope, checked against the receiver class.
scope: #local behaves identically to named: (this node's local
registry). scope: #global checks OTP's cluster-wide global
registry instead — the same name resolves to the same actor from
every connected node.
Error cases: as named:, plus type_error when scope is neither
#local nor #global.
Examples
s := (Scheduler named: #scheduler scope: #global) unwrap
Return every currently-registered Beamtalk actor as Actor proxies.
Filters out raw Erlang-registered processes (kernel, logger, user FFI
registrations) — only actors carrying the '$beamtalk_actor' marker
appear. Each returned proxy carries its real class, so
Actor allRegistered first class returns the concrete subclass, not
Actor.
Intended for tooling and REPL discovery; production code should use
Actor named: to address known names directly.
This lists only name-registered actors. To list every live actor
(named or not) use Node current actors; every registered actor is also
in that set.
Examples
Actor allRegistered // => #(an Actor(Counter), an Actor(Logger))
Spawn a new actor process on node, with default state.
Same contract as spawn, except the actor process starts on node
instead of here — initialize, reserved-name checks, and
ActorSpawned all fire there. See "Remote spawn and lookup" above for
the idempotency and linking caveats.
Error cases:
node_down—nodeis unreachableclass_not_found— the class is not loaded onnode
Examples
worker := (Node named: #'worker@localhost') unwrap
c := (Counter spawnOn: worker) unwrap
c node // => Node(worker@localhost)
Atomically spawn a new actor on node and register it under name
there.
Same contract as spawnAs:, except the actor process (and its
registration) both happen on node instead of here. See "Remote
spawn and lookup" above for the idempotency and linking caveats.
Error cases: as spawnAs:, plus node_down (node is unreachable)
and class_not_found (the class is not loaded on node).
Examples
worker := (Node named: #'worker@localhost') unwrap
(Counter spawnAs: #hits on: worker) unwrap
Spawn a new actor process on node, with initialization arguments.
Same contract as spawnWith:, except the actor process starts on
node instead of here. initArgs is wire-encoded (ADR 0126 §5.1)
before crossing the node boundary — a Value nested inside it is
enveloped and version-checked like any other cross-node argument; a
HandleScoped value (e.g. Ets) is rejected here, in the sender,
before the erpc call. See "Remote spawn and lookup" above for the
idempotency and linking caveats.
Error cases: as spawnOn:, plus not_serialisable (initArgs
contains a node-scoped handle).
Examples
worker := (Node named: #'worker@localhost') unwrap
c := (Counter spawnWith: #{#count => 10} on: worker) unwrap
Atomically spawn a new actor with init args on node and register it
under name there.
Same contract as spawnAs:on: plus the initialisation arguments
passed to the actor's init/1 callback — see spawnWith:on: for the
wire-encoding note.
Examples
worker := (Node named: #'worker@localhost') unwrap
(Counter spawnWith: #{#count => 10} as: #hits on: worker) unwrap
Look up a registered actor by name on node, checked against the
receiver class.
Same contract as named:, checked against node's registry instead
of this node's. The returned reference is node-qualified: it keeps
resolving against node regardless of which node later sends to it.
Error cases: as named:, plus node_down (node is unreachable).
Examples
worker := (Node named: #'worker@localhost') unwrap
hits := (Counter named: #hits on: worker) unwrap
hits increment // => 1 — sent to worker, not resolved locally
Return every currently-registered Beamtalk actor on node as Actor
proxies.
Same contract as allRegistered, listing node's registered actors
instead of this node's. Unlike the local, network-free
allRegistered, this answers a Result — reaching another node is an
expected-failure operation (ADR 0060).
Like allRegistered, this lists only name-registered actors. To list
every live actor on node (named or not) use node actors.
Examples
worker := (Node named: #'worker@localhost') unwrap
(Actor allRegisteredOn: worker) unwrap // => #(an Actor(Counter), ...)
Default OTP restart policy for this actor class.
Returns #temporary by default — the actor is not automatically restarted
on crash. Override in subclasses to declare a different default:
#permanent— always restart (e.g., a database pool)#transient— restart only on abnormal exit (e.g., a request handler)#temporary— never restart (the default)
Examples
Actor supervisionPolicy // => #temporary
DatabasePool supervisionPolicy // => #permanent (if overridden)
Whether this class is a supervisor.
Returns false for Actor subclasses. Supervisor and DynamicSupervisor
subclasses override this to return true. Used by SupervisionSpec childSpec
to determine OTP type (#worker vs #supervisor) and shutdown timeout.
Examples
Counter isSupervisor // => false
WebApp isSupervisor // => true (if WebApp subclasses Supervisor)
Return a SupervisionSpec for this actor class with default settings.
Creates a SupervisionSpec with actorClass set to the receiver and
restart set from supervisionPolicy. Use the fluent with*: API to
override per-child settings:
Examples
DatabasePool supervisionSpec
// => SupervisionSpec with actorClass: DatabasePool, restart: #temporary
DatabasePool supervisionSpec withId: #primary withArgs: #{#role => #primary}
Instance Methods
Register this (already-spawned) actor under name.
Non-atomic with respect to spawn: another process may claim name
between the actor's spawn and this call. Prefer class spawnAs: when
the name is known at spawn time.
On success returns Result ok: self so calls can be chained:
(counter registerAs: #c) onSuccess: [:c | c increment]
Unregister this actor's name, if any. Idempotent.
Returns #ok even when the actor has no registered name or the name has
already been released. When a real failure occurs (reserved name, type
error, etc.) the error is raised — consistent with other teardown methods
like stop and kill.
When an actor process exits, Erlang automatically releases its registered
name; callers do not need to call unregister from terminate:.
Examples
counter unregister // => #ok
Internal FFI seam (ADR 0101 Part 4): release this actor's registered
name. Keeps unregister pure Beamtalk. Returns bare ok, which the
FFI boundary auto-converts to Result ok: nil (ADR 0076/0121).
Return the Symbol this actor is registered under, or nil if unnamed.
Examples
counter registeredName // => #counter (or nil)
Whether this actor currently has a registered name.
Examples
counter isRegistered // => true or false
Wrap this actor with a custom message timeout.
Returns a TimeoutProxy that forwards all messages to this actor using
the given timeout (milliseconds, a Duration, or #infinity) for
gen_server:call. The default OTP timeout is 5000ms.
The proxy is a separate actor process — call stop on it when done.
Examples
slowDb := db withTimeout: 30000
slowDb query: sql // forwarded with 30s timeout
slowDb stop // stop the proxy when done
patient := db withTimeout: (Duration seconds: 30)
patient stop
Optional lifecycle hook called automatically after spawn.
Override in subclasses to perform setup that goes beyond state: defaults,
such as opening resources or computing derived state. Called synchronously
before the spawned object is returned to the caller.
If initialize raises an error, the spawn fails with a catchable
InstantiationError. Under a supervisor, the child start fails and the
supervisor applies its restart strategy.
Examples
Actor subclass: Stack
state: items = nil
initialize => self.items := #()
Optional lifecycle hook called when the actor is shutting down.
Override in subclasses to perform cleanup such as closing resources, flushing buffers, or notifying dependents. Called synchronously on:
- graceful shutdown (
stop) - a supervisor's direct
aSupervisor terminate: aClass - a supervisor-initiated shutdown of this actor as part of a
rest_for_one/one_for_allrestart cascade (this actor did not crash itself — a sibling did — but the supervisor tears it down too)
The reason parameter is an Object — it may be a Symbol like #normal
for graceful stop, but OTP can also pass compound terms like
{shutdown, term} for non-atom shutdown reasons.
Not called when the actor is forcefully killed (kill).
If terminate: raises an error, shutdown proceeds anyway.
Actor state (self.field) is accessible during terminate:.
BT-3596: overriding terminate: makes this actor's class (and any
subclass) trap exits (process_flag(trap_exit, true)), which is what
lets OTP deliver a supervisor's shutdown signal as a message instead of
killing the process before terminate: can run. A class that never
overrides terminate: keeps the plain link semantics it always had.
Examples
Actor subclass: Logger
state: logFile = nil
terminate: reason =>
self.logFile isNil ifFalse: [self.logFile close]
Delegate message dispatch to the backing Erlang module.
This method is a sentinel — non-native Actors do not have a backing
Erlang module, so calling delegate raises an Error at runtime.
Native Actors (declared with native: in ClassBuilder) override this
intrinsic via the compiler's codegen phase.
Deliberately left without a declared return type (BT-2862): the type
checker special-cases an unannotated self delegate body to stay
Dynamic, since a native override's real return type depends on
whichever backing module it delegates to.
Examples
counter delegate // => ERROR: delegate called on a non-native Actor
Return the raw Erlang PID backing this actor.
Useful for FFI interop where an Erlang function expects a raw pid.
Examples
rawPid := counter pid
rawPid class // => Pid
The Node this actor lives on (ADR 0126 §2).
Answered by the actor itself, so a TimeoutProxy reports its target's
node rather than its own (ADR 0126 §6).
Examples
counter node // => Node(nonode@nohost)
counter node isCurrent // => true
Whether this actor lives on a different node from the caller (ADR 0126 §2).
The runtime answers a send of isRemote in the caller's process —
asking the actor for its node and comparing it with the caller's node —
because the actor itself can only ever see its own node. This body only
runs for a self-send, where the caller is the actor, so it is always
false there.
Examples
counter isRemote // => false
Create an Erlang monitor on this actor's process.
Returns a Reference that can be used to cancel the monitor via
demonitor. The caller will receive a DOWN message if the
actor exits.
Examples
ref := counter monitor
ref class // => Reference
ref demonitor // cancel the monitor
Register a callback to be invoked when this actor exits.
Monitors the actor and calls block value: reason when the actor
process terminates. Returns #ok immediately. The block is called
asynchronously from a lightweight watcher process.
Examples
worker onExit: [:reason |
Logger info: "worker exited" metadata: #{"reason" => reason displayString}
]
Gracefully stop this actor (gen_server:stop).
Idempotent: stopping an already-stopped actor succeeds silently. Raises an error if the actor times out during shutdown.
Examples
counter stop // => ok
Forcefully kill this actor (exit(Pid, kill)).
Unlike stop, kill cannot be trapped by the actor process.
Examples
counter kill // => ok
Check if this actor's process is still alive.
WARNING: isAlive check-then-act is inherently racy. The actor could die between the isAlive check and a subsequent message send. Use monitors for robust lifecycle management.
Examples
counter isAlive // => true or false
Inherited Methods
From Object
Return the class of the receiver.
Examples
42 class // => Integer
"hello" class // => String
Test if the receiver is nil. Returns false for all objects except nil.
Examples
42 isNil // => false
nil isNil // => true
Test if the receiver is not nil. Returns true for all objects except nil.
Examples
42 notNil // => true
nil notNil // => false
If the receiver is nil, evaluate nilBlock. Otherwise return self.
_nilBlock is never evaluated on this Object-level definition (only
UndefinedObject's override calls it, with Block(R) -> R), so R is
unconstrained here — parameterizing it still documents the zero-arg
shape without implying this branch produces R (BT-2834).
Examples
42 ifNil: [0] // => 42
nil ifNil: [0] // => 0
If the receiver is not nil, evaluate notNilBlock with self.
Left as a bare Block deliberately (BT-2834): notNilBlock is invoked
one-arg with self and its result becomes the method's own result, so
a precise signature needs a Self-in-Block(...) type-arg form
(Block(Self, R) -> R) with no precedent elsewhere in stdlib. The type
checker doesn't consult this declared signature for ifNotNil: anyway
— it narrows the block's parameter from the receiver's own type via
infer_args_for_if_not_nil in inference.rs (BT-2046), independent of
this stdlib declaration.
Examples
42 ifNotNil: [:v | v + 1] // => 43
nil ifNotNil: [:v | v + 1] // => nil
If nil, evaluate nilBlock; otherwise evaluate notNilBlock with self.
Bare Block params here (unlike UndefinedObject's Block(R) -> R
override, BT-2824) are intentional, not an oversight: the type checker
never consults this declared signature for these two selectors — it
reads the block arguments' actual inferred return types directly
(if_nil_branch_union_ret_ty in inference.rs, BT-2047) since a bare
-> R here can't express "whatever the block returns" without a
Self-in-Block(...) type-arg form with no precedent elsewhere in
stdlib.
Examples
42 ifNil: [0] ifNotNil: [:v | v + 1] // => 43
nil ifNil: [0] ifNotNil: [:v | v + 1] // => 0
If not nil, evaluate notNilBlock with self; otherwise evaluate nilBlock.
See ifNil:ifNotNil: above — the bare Block params are intentional.
Examples
42 ifNotNil: [:v | v + 1] ifNil: [0] // => 43
nil ifNotNil: [:v | v + 1] ifNil: [0] // => 0
Return the developer-readable (Debug) string representation.
printString is the Debug protocol (ADR 0094): the self-describing,
structural form used by the REPL, logs, and by any other printString
that nests this object. It is the REPL default — evaluating an expression
shows its printString.
This default returns the bare class name (no a/an article — the
old "a ClassName" form was dropped in ADR 0094). Value overrides it
with the structural ClassName(field: value, ...) form, actors render as
Actor(ClassName, pid), supervisors as Supervisor(ClassName, pid) /
DynamicSupervisor(ClassName, pid), and primitive types (Integer, String,
List, …) override it with their own richer output. Authors rarely override
printString directly — the default is derived.
Examples
42 printString // => "42"
Return the user-facing (Display) string representation.
displayString is the Display protocol (ADR 0094): the human-facing
form. It is the hook the language pulls during string interpolation —
every {...} segment renders via the value's displayString. Developers
rarely call it directly; they override it when a value has a natural
human rendering (e.g. Money → $10.50, where printString would still
show the Debug form).
It defaults to printString, so most types need no override. String
and Symbol demonstrate the split: "hi" printString → "\"hi\""
(quoted, Debug) while "hi" displayString → "hi" (plain, Display);
likewise #foo drops its # prefix under displayString.
displayString is not part of the Printable protocol (deferred per
ADR 0094 §5).
Examples
42 displayString // => "42"
Open a navigable Inspector cursor on the receiver.
ADR 0095 Phase 3 (BT-2504). inspect is repurposed from -> String
(the ADR-0094 deferral) to the verb that produces an Inspector — a
live, immutable cursor for drilling into the object (Inspector on: self).
anObject inspect is the shorthand; Inspector on: anObject is the
explicit spelling. The cursor exposes fields/at:/path/refresh/
printString (an indented text tree) and asDictionaries (the MCP/browser
wire form); see Inspector.
This is a breaking change: code that used inspect for its old
String result must switch to printString (the structural Debug string,
ADR 0094) — a transitional lint flags inspect used directly in ++/
string position.
Examples
42 inspect kind // => #value
(Point x: 3 y: 4) inspect fields size // => 2
(Point x: 3 y: 4) printString // => "Point(x: 3, y: 4)" (the old inspect string)
Return the receiver itself. Useful for cascading side effects.
Examples
42 yourself // => 42
Return a hash value for the receiver.
Examples
42 hash
Test value equality — the overridable counterpart to =:=.
Defaults to =:= (Erlang structural term equality), so for most classes
the two agree. Unlike =:=, this is an ordinary message send, so a class
whose logical value is not its representation can override it: a
persistent structure whose shape depends on construction history, a
timestamp that should compare by instant rather than by wall-clock fields,
or a type with a normalising form. =:=, =/=, == and /= are lowered
straight to Erlang BIFs (ADR 0002) and cannot be overridden — the compiler
rejects any attempt (BT-2997).
What honours it
The linear scans: includes: on Collection, List, Array and
Dictionary (which searches values), plus List>>indexOf: and
TestCase>>assert:equals:.
Contract
An override must agree with =:= wherever =:= holds — a =:= b must
imply a equals: b. It may only make more values equal, never fewer.
The scans above rely on this: each tries raw =:= first and dispatches
only on a miss, so a fast-path hit is always a genuine equals: hit.
Caveat
Set, Dictionary keys, and List>>unique decide identity in the VM
with raw =:=, never through this method — keying and deduplication need
an order or a hash that a user-defined equals: cannot supply. A class
that overrides equals: still deduplicates by representation when used
as a key or element. Normalise the representation if that matters.
Examples
42 equals: 42 // => true
42 equals: 42.0 // => false (defaults to strict `=:=`)
"ab" equals: "ab" // => true
Test if the receiver responds to the given selector.
Examples
42 respondsTo: #abs // => true
Return the names of fields.
Examples
42 fieldNames // => #()
Return the value of the named field.
Left without a declared return type deliberately (BT-2834 pattern,
see Block>>value): the field's actual value can be any type, and
callers routinely send it further messages ((self fieldAt: #x) + 1)
that a fixed -> Object would statically reject.
Examples
object fieldAt: #name
Set the value of the named field (returns new state).
Examples
object fieldAt: #name put: "Alice"
Test whether the receiver has the named field. Never raises — the
non-raising counterpart to fieldAt:, in particular for a late
slot (ADR 0124): fieldAt: raises UninitializedStateError reading
an unassigned late slot, hasField: is the question to ask first.
Examples
c := CodexClient start: "/tmp/ws" config: cfg onEvent: nil
c hasField: #proc // => false — declared `late`, not yet assigned
c launch
c hasField: #proc // => true
42 hasField: #anything // => false — primitives have no fields
Return a late field to the unassigned state (ADR 0124), so it can be
assigned again (e.g. re-acquiring a resource). Joins fieldAt:/
fieldAt:put:/fieldNames in ADR 0035's field-prefixed reflection
family. Returns self.
Examples
c clearField: #proc
c hasField: #proc // => false — relaunchable
Send a unary message dynamically.
Left without a declared return type deliberately (BT-2834 pattern,
see fieldAt: above): the invoked method's return type is unknown
statically, and a fixed -> Object would block legitimate chained
sends on the real (narrower) result.
Examples
42 perform: #abs // => 42
Send a message dynamically with arguments.
See perform: above for why no return type is declared.
Examples
3 perform: #max: withArguments: #(5) // => 5
Raise an error indicating this method must be overridden by a subclass.
Examples
self subclassResponsibility
Raise an error indicating this method has not yet been implemented.
Use this for work-in-progress stubs. Distinct from subclassResponsibility,
which signals an interface contract violation.
Examples
self notImplemented
Send aValue to Transcript without a trailing newline.
Outside an interactive workspace this is a Logger notice (ADR 0129 §5).
Examples
42 show: "value: "
Send aValue to Transcript followed by a newline.
Outside an interactive workspace this is a Logger notice (ADR 0129 §5).
Examples
42 showCr: "hello world"
Test if the receiver is an instance of aClass or any of its subclasses.
For class-object receivers, follows Smalltalk semantics: self class
is the metaclass, so the check walks the parallel metaclass hierarchy.
The parallel chain is grounded at ProtoObject class superclass == Class
(ADR 0036), so the metaclass tower merges into the instance-side
Class → Behaviour → Object → ProtoObject chain. As a result,
Integer isKindOf: Object and Integer isKindOf: Class both return true.
Examples
42 isKindOf: Integer // => true
42 isKindOf: Object // => true
#foo isKindOf: Symbol // => true
#foo isKindOf: String // => false
Integer isKindOf: Number // => false (metaclass chain, not instance chain)
Integer isKindOf: Number class // => true (Number class is in the parallel chain)
Integer isKindOf: Object // => true (grounded — Object is reachable via the metaclass tower)
Integer isKindOf: Class // => true (Integer class inherits from Class)
Raise an error with the given message.
Examples
self error: "something went wrong"
Delegate message dispatch to the backing Erlang module (ADR 0101, BT-2720).
This method is a sentinel — a plain Object has no backing Erlang module,
so calling delegate raises an Error at runtime. Stateless Objects
declared with native: have their self delegate method bodies rewritten
by the compiler's codegen phase to call the backing module directly, so the
sentinel is never reached on a native: class.
Unlike Actor's delegate (visible only to Actor subclasses), this
Object-base sentinel is visible to every class, so delegate is a
reserved selector on the Object protocol.
Examples
42 delegate // => ERROR: delegate called on a non-native Object
From ProtoObject
Test value equality (Erlang ==, non-strict — 1 == 1.0 is true).
Examples
42 == 42 // => true
"abc" == "abc" // => true
Test value inequality (negation of ==).
Examples
1 /= 2 // => true
42 /= 42 // => false
Test strict value equality (Erlang =:= — 1 =:= 1.0 is false, unlike ==).
Every object supports this at runtime (it lowers directly to Erlang's
=:= operator regardless of receiver type, ADR 0002) — declared here,
once, so it is visible in method listings/completions/respondsTo: for
every class, not just the handful (Integer, String, ...) that
separately redeclare it with a narrower, class-typed other for
documentation purposes.
Examples
1 =:= 1 // => true
1 =:= 1.0 // => false (strict — different types)
(Dictionary new) =:= #{} // => true
Test strict value inequality (negation of =:=).
Examples
1 =/= 1.0 // => true (strict — different types)
1 =/= 1 // => false
Return the class of the receiver.
Examples
42 class // => Integer
"hello" class // => String
Handle messages the receiver does not understand. Override for custom dispatch.
No declared return type (BT-2834 pattern, see Object>>fieldAt:): a
custom override can return any type, and callers chain further sends
on the result that a fixed -> Object would statically reject.
Examples
42 unknownMessage // => ERROR: does_not_understand
Send a message dynamically with an arguments list.
Examples
42 perform: #abs withArguments: #() // => 42
Execute a class method in the caller's process, bypassing gen_server dispatch.
The caller takes responsibility for knowing the method does not mutate class state. Useful for long-running class methods that would otherwise block the class object's gen_server.
Limitations: only resolves methods defined directly on the target class
module (does not walk the superclass chain). Class variables and self
are not available to the method (nil and #{} are passed).
Examples
MyClass performLocally: #run:ctx: withArguments: #(input, ctx)
Send a message dynamically with an arguments list and explicit timeout.
The timeout (in milliseconds or #infinity) applies to the gen_server:call
when the receiver is an actor. For value types, timeout is ignored.
Examples
actor perform: #query withArguments: #(sql) timeout: 30000