Inspector
Inspector — a live, immutable cursor for navigating into a single object (ADR 0095).
Where the three navigators (SystemNavigation, ProcessNavigation,
AnnouncementNavigation) each enumerate a system-wide collection, an
Inspector is a cursor over one object that drills into it — the
Beamtalk equivalent of a Pharo Inspector. Inspector on: anObject returns a
cursor; fields lists its drillable InspectorField records; at: drills
into one field, returning a new child cursor; parent/root/path walk the
drill chain; refresh re-captures a fresh snapshot of the same subject.
Inspector on: anObject is the explicit entry point; anObject inspect is
the shorthand (ADR 0095 Phase 3, BT-2504, repurposed inspect from String
to Inspector). The MCP/browser surfaces render asDictionaries /
asDictionary (the typed wire form); the REPL renders printString.
The cursor is immutable. Every navigation message returns a new
Inspector — at: returns a child cursor (with parent set), refresh
returns a cursor on a freshly-captured snapshot. Nothing mutates in place, so
a UI can hold several cursors at once.
kind-polymorphic, one class. A single Inspector carries a kind tag
(#value structural, #actor snapshot) rather than a subclass per kind —
the same fork ADR 0092's SupervisionNode settled. v1 covers #value (pure
structural traversal of a Value's field: slots, ADR 0094 sort order) and
#actor (a lazy, timeout-guarded sys:get_state snapshot of an Actor's
state). A wedged, dead, or non-sys actor degrades to a single
InspectorField name: #status value: #unavailable — never a crash.
(#collection/#foreign and value evaluate: are later phases, ADR 0095
§6–§7.)
Inspector is a sealed typed Object subclass: a live cursor handle, like
the navigators. Its carried state (subject, kind, snapshot, parent, path)
lives in a runtime-minted handle — not in declarable field: slots (an
Object subclass has none, ADR 0067) — and is read back through the
beamtalk_inspector shim. The records it returns (InspectorField) are
the Values.
Examples
i := Inspector on: (Point x: 3 y: 4)
i kind // => #value
i fields size // => 2
(i at: #x) unwrap subject // => 3
i path // => #()
Methods
Class Methods
Open an inspector cursor on anObject (ADR 0095 §1).
For an actor, the timeout-guarded sys:get_state snapshot is captured
here, at construction; for a value the subject is taken structurally with no
process contact. anObject inspect is the shorthand for Inspector on: anObject (ADR 0095 §1).
Examples
Inspector on: (Point x: 3 y: 4) // => an Inspector
Inspector on: aCounter // => an Inspector (#actor)
Instance Methods
The inspected object — the value itself, or, for an actor, the captured state snapshot.
Examples
(Inspector on: (Point x: 3 y: 4)) subject // => Point(x: 3, y: 4)
The subject kind: #value (structural), #actor (snapshot),
#collection (windowed elements/associations, ADR 0095 §6), or #foreign
(a non-Beamtalk OTP process, ADR 0095 §3).
Examples
(Inspector on: (Point x: 3 y: 4)) kind // => #value
(Inspector on: aCounter) kind // => #actor
(Inspector on: #(1, 2, 3)) kind // => #collection
The drillable fields of the inspected object, as immutable
InspectorField records (ADR 0095 §2–§3, §6).
For a #value, these are its field: slots in ADR-0094 sort order. For an
#actor, they are its user state slots from a lazy, timeout-guarded
snapshot — or, if the actor is busy/dead/non-sys, the single diagnostic
InspectorField name: #status value: #unavailable (never a crash). For a
#collection, they are the current window of elements (default page
size 50, ADR 0095 §6) — ordered collections render #element fields keyed
by 1-based index, keyed collections (Dictionary/Bag) render
#association fields keyed by the entry key; use page: for the next
window. For a #foreign process, they are best-effort #processInfo data.
Examples
(Inspector on: (Point x: 3 y: 4)) fields size // => 2
(Inspector on: ((1 to: 1000) asList)) fields size // => 50
The cheap full element count of a #collection cursor — the collection's
size, not a window walk (ADR 0095 §6). For a non-collection cursor it
is the number of fields (already cheap).
Examples
(Inspector on: ((1 to: 100000) asList)) size // => 100000
(Inspector on: (Point x: 3 y: 4)) size // => 2
Internal FFI seam (ADR 0101 Part 4): the cheap element count of a
#collection cursor. Keeps size pure Beamtalk.
Drill into one field by name, returning a child Inspector cursor on that
field's value (ADR 0095 §1).
Returns a Result: Result error: (beamtalk_error no_such_field) when no
field has that name. The child cursor's parent is this cursor, and its
path extends this one's by the drilled name.
Examples
(Inspector on: (Point x: 3 y: 4)) at: #x // => Result ok: (an Inspector on 3)
(Inspector on: (Point x: 3 y: 4)) at: #zzz // => Result error: no_such_field
Return a new Inspector cursor on the aPage-th window of a #collection
(1-based, ADR 0095 §6).
page: 1 is the first window (the elements fields shows on a fresh
cursor); page: 2 is the next 50-element window, and so on. A page past the
end yields a cursor whose fields is empty. The original cursor is
unchanged (immutable). Sending page: to a non-collection cursor returns
Result error: (beamtalk_error not_a_collection).
Examples
big := Inspector on: ((1 to: 1000) asList)
(big page: 2) unwrap fields first value // => 51
Evaluate the Beamtalk expression src against the inspected object, with
self bound to the value (ADR 0095 §1, §7).
For a #value or #collection cursor, src is compiled and evaluated with
self bound to the subject — a pure compiled dispatch — returning
Result ok: theValue (or Result error: if the expression fails to compile
or raises). For an #actor or #foreign cursor it returns
Result error: (beamtalk_error actor_eval_unsupported): actor
evaluate-in-context cannot bind self to a frozen snapshot and is a deferred
follow-up (ADR 0095 §7) — drill with at: instead.
Examples
i := Inspector on: (Point x: 3 y: 0)
(i evaluate: "self x + 10") unwrap // => 13
ci := Inspector on: aCounter
(ci evaluate: "self increment") isError // => true (actor_eval_unsupported)
The cursor this one was drilled from, or nil at the root.
Examples
child := ((Inspector on: (Point x: 3 y: 4)) at: #x) unwrap
child parent kind // => #value
The top of this drill chain — the cursor with no parent.
Examples
child := ((Inspector on: (Point x: 3 y: 4)) at: #x) unwrap
child root kind // => #value
The breadcrumb of drilled names from the root to this cursor (for UI).
#() at the root.
Examples
child := ((Inspector on: (Point x: 3 y: 4)) at: #x) unwrap
child path // => #(#x)
A cursor on a freshly-captured snapshot of the same subject (ADR 0095 §5). The original cursor is unchanged (immutable).
refresh re-snapshots this cursor's own subject, which differs by how
the cursor was reached:
- A root
#actorcursor re-issues the guardedsys:get_state, picking up state change since the previous snapshot. - A
#valuecursor (including one drilled into an actor's slot withat:) re-freezes its already-captured value — structurally identical, since per ADR 0095 §4 drilling never re-contacts the live actor (the sub-tree is a consistent point-in-time view). To see a drilled actor field move,refreshthe root actor cursor and drill again.
Examples
ci := Inspector on: aCounter // snapshot at T0
aCounter increment // state moves on
ci refresh fields // a fresh cursor at T1 (root actor re-fetched)
An indented text tree of the cursor and its immediate fields, one level deep (ADR 0095 §7). The REPL default.
Examples
(Inspector on: (Point x: 3 y: 4)) printString
// => "Inspector(Point)\n x: 3\n y: 4"
An indented text tree of the cursor to depth levels (ADR 0095 §7).
depth: 1 is the immediate fields; deeper expands drillable children.
Examples
(Inspector on: (Point x: 3 y: 4)) printStringExpanded: 1
The cross-surface wire form — one Dictionary per drillable field
(ADR 0095 §7). The MCP/browser surfaces render this structure so every
surface shows equivalent data.
Each dictionary carries the field's named, typed slots: #name (the
navigation key to pass back to at:), #label, #value (a JSON-stable
display string), #kind, and #drillable. This is the same typed-record
pattern SupervisionTree asDictionaries (ADR 0092) uses. For the
cursor-level envelope (kind/path/childCount/page), use asDictionary.
Examples
(Inspector on: (Point x: 3 y: 4)) asDictionaries
// => #(#{#name => #x, #label => "x", #value => "3", ...}, ...)
The whole cursor as a single wire-form Dictionary (ADR 0095 §7) — the
navigable-node envelope the MCP/browser surfaces render and page through.
Carries #kind (the subject kind), #path (the breadcrumb of drilled keys
from the root), #childCount (the cheap full element count for a
#collection, else the field count), #page (the 1-based window index),
and #fields (the asDictionaries window of field records). A surface
fetches the next window by re-issuing page: and re-serialising.
Examples
((1 to: 1000) asList inspect) asDictionary
// => #{#kind => #collection, #childCount => 1000, #page => 1, #fields => #(...), ...}
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.
Examples
42 ifNil: [0] // => 42
nil ifNil: [0] // => 0
If the receiver is not nil, evaluate notNilBlock with self.
Examples
42 ifNotNil: [:v | v + 1] // => 43
nil ifNotNil: [:v | v + 1] // => nil
If nil, evaluate nilBlock; otherwise evaluate notNilBlock with self.
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.
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 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.
Examples
object fieldAt: #name
Set the value of the named field (returns new state).
Examples
object fieldAt: #name put: "Alice"
Send a unary message dynamically.
Examples
42 perform: #abs // => 42
Send a message dynamically with arguments.
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 the current transcript without a trailing newline.
Nil-safe: does nothing when no transcript is set (batch compile, tests).
Examples
42 show: "value: "
Send aValue to the current transcript followed by a newline.
Nil-safe: does nothing when no transcript is set (batch compile, tests).
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 ==).
Examples
42 == 42 // => true
"abc" == "abc" // => true
Test value inequality (negation of ==).
Examples
1 /= 2 // => true
42 /= 42 // => 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.
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