WorkspaceInterface

Inherits from Object
Sealed

WorkspaceInterface — Per-workspace actor introspection and binding container.

Modeled on Squeak 5.2+'s Environment class: a scoped container for workspace-level bindings and actor introspection. Each workspace has its own WorkspaceInterface instance.

The singleton instance is set during workspace bootstrap (ADR 0019 Phase 2). The convenience binding Workspace is also available after bootstrap.

Examples

WorkspaceInterface current actors
WorkspaceInterface current actorAt: "<0.132.0>"
WorkspaceInterface current actorsOf: Counter

Class Methods

current source

Return the current singleton instance.

Instance Methods

actors source

Return a list of all live actors as object references.

Examples

Workspace actors
processes source

Return a navigable snapshot of the live supervision tree (ADR 0092).

The dynamic counterpart of Workspace actors: a SupervisionTree over the workspace application tree with runtime plumbing filtered out (the default scope, == ProcessNavigation default tree). Walk it (do:, select:, findClass:), reach the head (root), or take the flat record list (nodes). The snapshot is best-effort point-in-time — re-call to refresh.

The default-scope reach is an ADR-0091 Read op, scoped exactly like actors. For the privileged whole-node view use ProcessNavigation system tree.

Examples

Workspace processes root                  // => the snapshot root SupervisionNode
Workspace processes do: [:n | Transcript showLine: n printString]
Workspace processes findClass: Counter    // => List(SupervisionNode)
Workspace processes nodes                 // => the flat List(SupervisionNode)
actorAt: pidString source

Look up a specific actor by pid string.

Examples

Workspace actorAt: "<0.132.0>"
actorsOf: aClass source

Return all actors of a given class.

Examples

Workspace actorsOf: Counter
classes source

Return a list of loaded user classes with source file info.

Examples

Workspace classes
testClasses source

Return classes that are TestCase subclasses.

Examples

Workspace testClasses
globals source

Return a live BindingsView over the project namespace (ADR 0081).

Reads reflect the current workspace singletons (Transcript, Beamtalk, Workspace) and user-registered bindings (bind:as:). It is the same BindingsView type as Session current bindings, so both binding layers share one Dictionary protocol (at:, at:put:, removeKey:, includesKey:, keys, values, size, do:).

Writes are synchronous: at:put: routes through bind:as: and removeKey: through unbind:, inheriting their protected-name conflict checks. (Beamtalk globals — the class registry — stays a Dictionary; it is not a mutable binding layer.)

Examples

Workspace globals at: #Transcript     // => the Transcript singleton
Workspace globals includesKey: #Beamtalk   // => true
currentSession source

Return the calling session as a Session, or nil outside a REPL eval (ADR 0081 Phase 5).

A navigation alias for Session current: it returns the identical value (the same Session carrying this session's id and bindings, or nil when there is no session — e.g. compiled program code). There is no hasSession predicate; guard with Workspace currentSession isNil or Session current ifNotNil: [:s | ...].

Examples

Workspace currentSession                  // => a Session (in REPL) | nil
Workspace currentSession isNil            // => false (in REPL)
sessions source

Return a List of Session values, one per live shell (ADR 0081 Phase 7).

Each value is minted the same way Session withId: does, so it works with the instance reads (s id, s bindings keys) and rejects cross-session writes. This closes the gap left by Session withId: (which assumes you already know the id): tooling discovers live session ids with Workspace sessions collect: [:s | s id].

Examples

Workspace sessions                        // => #(a Session)
Workspace sessions collect: [:s | s id]   // => #("user-repl-abc-123")
sync source

Sync the project: incrementally compile all changed files.

Scans the current working directory for a beamtalk.toml project manifest, then finds .bt and .erl files, compiles changed files in dependency order, and returns a result Dictionary.

The result Dictionary contains:

  • #summary — human-readable summary String
  • #classes — List of loaded class name Strings
  • #errors — List of error Dictionaries (empty on success)
  • #changedCount — number of files reloaded
  • #unchangedCount — number of unchanged files
  • #deletedCount — number of deleted files

Examples

Workspace sync           // => #{#summary => "Reloaded 2 of 5 files (3 unchanged)", ...}
(Workspace sync) at: #summary  // => "Reloaded 2 of 5 files (3 unchanged)"
changes source

Return the workspace ChangeLog — the navigable view of pending in-memory changes (ADR 0082).

All pending-state queries live on the returned ChangeLog object, following Pharo's Smalltalk changes idiom: "is anything dirty?" is Workspace changes notEmpty; "what's dirty?" is Workspace changes dirtyMethods.

Examples

Workspace changes notEmpty       // => false
Workspace changes dirtyMethods   // => #{}
load: path source

Compile and load a .bt file, registering the class. Returns a List of loaded class objects (empty if none resolved). Returns a structured error if path is not a String or the file cannot be loaded.

Examples

Workspace load: "examples/counter.bt"  // => [Counter]
newClass: source at: path source

Create a brand-new class from a source String at a target path (ADR 0082).

Compiles and installs the class in memory, then records a durable kind: #'new-class' ChangeLog entry. Phase 1 does NOT write the file to disk — that happens later on Workspace flush, which replays the entry to write the initial file. Returns the loaded class object(s), like load:.

Raises a loud, specific error (no silent fallback) when:

  • path already exists on disk;
  • path lies outside the project source tree;
  • the declared class name does not match the basename of path (one class per file, ADR 0040);
  • a class of that name is already loaded (use compile:source: instead).

Examples

Workspace newClass: "Object subclass: Greeter" at: "src/greeter.bt"
// => [Greeter]
flush source

Flush every pending durable Tier 1 change to disk (ADR 0082 Phase 2; destructive tiering ADR 0113 Phase 2).

Writes each pending Tier 1 ChangeLog entry's patched body back to its source file via byte-span splice (trivia-preserving — no AST reprint), atomically (<file>.tmp + atomic rename), with external-edit conflict detection. Tier 1 covers every entry that edits a still-existing file: patches, newClass:at: creations, and removeSelector: removals (ADR 0112) — excising a recorded span leaves the file in place, the same mechanical shape as a patch.

A pending Tier 2 entry (removeFromSystem's #'remove-class', ADR 0113) is not applied here — it deletes a file, and reaching that tier always requires an explicit confirmDestructive gesture (see flushIncludingDestructive / flush:confirmDestructive: below). It is instead reported in skipped with #reason => #destructive.

Returns a FlushResult summary recording what was written, what was skipped, and what conflicted:

  • flushed (Integer) — number of durable entries written
  • files (List of String) — files touched (written or removed)
  • newClasses (Integer) — subset of flushed for new-class entries
  • removedClasses (Integer) — subset of flushed for remove-class entries
  • skipped (List) — pending entries left out of this flush, e.g. #{#seq => ..., #class => ..., #reason => #destructive}
  • conflicts (List) — per-file conflict descriptors (#external_edit, #target_exists, #span_out_of_range, ...). A non-empty list means the listed entries remain pending and require manual reconciliation.

Quiet on success; loud only on conflicts and structured errors.

Examples

Workspace flush          // => _ (flush summary)
flush: filter source

Flush the pending Tier 1 ChangeLog entries matching filter (ADR 0082 Phase 2).

filter is one of:

  • a Class — flush only entries targeting that class
  • a Symbol (e.g. #'new-class') — flush only entries of that kind
  • a Dictionary #{#file => "path"} — flush only entries against that source file

A matching Tier 2 (#'remove-class') entry is reported in skipped, not applied — use flush:confirmDestructive: to also apply it within this scope. Returns the same FlushResult shape as flush.

Examples

Workspace flush: Counter             // => _
Workspace flush: #'new-class'        // => _
Workspace flush: #{ #file => "src/counter.bt" }  // => _
flush: filter confirmDestructive: confirmDestructive source

Flush the pending ChangeLog entries matching filter, additionally applying Tier 2 (#'remove-class') entries within that scope (ADR 0113 Phase 2).

confirmDestructive must be a literal true/false — never read from a workspace setting or environment variable, so reaching the destructive tier always names its own consent at the call site. The class/kind/file argument gives confirmDestructive: a real keyword partner, keeping this an ordinary two-keyword message.

Examples

Workspace flush: Counter confirmDestructive: true   // => _
flushIncludingDestructive source

Flush every pending durable+flushable ChangeEntry, Tier 1 and Tier 2 (ADR 0113 Phase 2).

The unscoped destructive-flush entry point — a bare unary selector, not a keyword message, since there is no class/kind/file argument to attach a confirmDestructive: keyword to once the call has no scope. Applies every pending Tier 1 entry (as flush does) plus every pending Tier 2 (#'remove-class') entry, deleting the corresponding .bt files.

Returns the same FlushResult shape as flush, with skipped empty for every entry this call was able to reach.

Examples

Workspace flushIncludingDestructive   // => _
recheckImage source

Whole-image re-check (ADR 0105 Phase 3): re-check every live class the workspace has a recorded source for, not just the dependents of one changed selector.

The "complete but unbounded" path ADR 0105 keeps out of the automatic post-reload check — that check only re-checks the xref-filtered dependents of whichever selector just changed, capped per reload; this is an explicit, on-demand sweep of the whole image with no cap. Also available as the REPL :recheck image alias.

Returns a Dictionary:

  • checked (Integer) — classes a re-check round-trip completed for
  • stale (Integer) — distinct classes with at least one finding
  • findings (List of Dictionary) — each with owner, severity, category, message, start, end

Examples

Workspace recheckImage          // => _ (checked/stale summary)
test source

Run all loaded test classes and return a TestResult.

Examples

Workspace test
test: testClass source

Run a specific test class and return a TestResult.

Examples

Workspace test: CounterTest
bind: value as: bindingName source

Register a value in the workspace namespace under a given name. Subsequent REPL evals can reference the value by name. Raises an error if name exists in Beamtalk globals (system name conflict). Warns if name is an existing loaded class (use reload instead).

Examples

Workspace bind: myActor as: #MyTool
unbind: bindingName source

Remove a registered name from the workspace namespace. Raises an error if name is not found.

Examples

Workspace unbind: #MyTool
supervisor source

Return the OTP application root supervisor, or nil if not configured.

Returns the Supervisor instance registered when a package with [application] supervisor = "ClassName" starts. Returns nil if the application has not been started with an OTP supervisor root.

Examples

Workspace supervisor                  // => Supervisor(AppSup, 0.200.0)
Workspace supervisor children         // => #(#DatabasePool #HTTPRouter)
startSupervisor: aClass source

Start and attach a supervisor to the workspace supervision tree.

The class must be a Supervisor or DynamicSupervisor subclass. Idempotent: returns the existing instance if already attached. Supports iterative development — stop and re-attach after reloading.

Examples

Workspace startSupervisor: MySup      // => Supervisor(MySup, 0.200.0)
Workspace startSupervisor: MySup      // => Supervisor(MySup, 0.200.0)  (idempotent)
stopSupervisor: aClass source

Stop and remove a supervisor from the workspace.

Works for both workspace-attached supervisors and the root application supervisor. Cleanly shuts down the supervisor and all its children. Raises an error if the supervisor is not running or not visible.

Examples

Workspace stopSupervisor: MySup       // => nil
supervisors source

List all supervisors visible from the workspace.

Returns the root application supervisor (if registered) and all supervisors attached via startSupervisor:. The root supervisor is not part of the workspace supervision tree but is included for discoverability.

Examples

Workspace supervisors                 // => #(Supervisor(MySup, 0.200.0))
autoflush source

Read the autoflush workspace setting (ADR 0082 Phase 4).

Default is false. When true, every successful durable in-memory patch (via >> / compile:source: / newClass:at:) immediately triggers a Workspace flush after the install, collapsing the model to write-through at the workspace level. The setting persists across workspace restarts.

Autoflush is best-effort: on flush failure (external-edit conflict, write error) memory and disk diverge and the ChangeEntry remains in the log for manual reconciliation — the BEAM module install is not rolled back (live actors may hold references to the new closures).

Examples

Workspace autoflush   // => false
autoflush: enabled source

Set the autoflush workspace setting (ADR 0082 Phase 4).

enabled must be a Boolean. Persists to the workspace metadata.json so the setting survives workspace restart. Returns the new value so the caller can confirm the effective state.

Examples

Workspace autoflush: true    // => true
Workspace autoflush: false   // => false
dependencies source

Return a Dictionary of direct dependency packages for the current workspace.

Keys are package name Strings, values are Package objects. Returns an empty Dictionary if the workspace has no declared dependencies.

Examples

Workspace dependencies
// => _

Inherited Methods

From Object

class

Return the class of the receiver.

Examples

42 class              // => Integer
"hello" class         // => String
isNil

Test if the receiver is nil. Returns false for all objects except nil.

Examples

42 isNil              // => false
nil isNil             // => true
notNil

Test if the receiver is not nil. Returns true for all objects except nil.

Examples

42 notNil             // => true
nil notNil            // => false
ifNil: _nilBlock

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
ifNotNil: notNilBlock

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
ifNil: _nilBlock ifNotNil: notNilBlock

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
ifNotNil: notNilBlock ifNil: _nilBlock

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
printString

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"
displayString

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"
inspect

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)
yourself Sealed

Return the receiver itself. Useful for cascading side effects.

Examples

42 yourself            // => 42
hash

Return a hash value for the receiver.

Examples

42 hash
equals: other

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
respondsTo: selector Sealed

Test if the receiver responds to the given selector.

Examples

42 respondsTo: #abs    // => true
fieldNames Sealed

Return the names of fields.

Examples

42 fieldNames             // => #()
fieldAt: name Sealed

Return the value of the named field.

Examples

object fieldAt: #name
fieldAt: name put: value Sealed

Set the value of the named field (returns new state).

Examples

object fieldAt: #name put: "Alice"
perform: selector Sealed

Send a unary message dynamically.

Examples

42 perform: #abs       // => 42
perform: selector withArguments: args Sealed

Send a message dynamically with arguments.

Examples

3 perform: #max: withArguments: #(5)   // => 5
subclassResponsibility

Raise an error indicating this method must be overridden by a subclass.

Examples

self subclassResponsibility
notImplemented

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
show: aValue

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: "
showCr: aValue

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"
isKindOf: aClass

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)
error: message

Raise an error with the given message.

Examples

self error: "something went wrong"
delegate Sealed

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

== other

Test value equality (Erlang ==, non-strict — 1 == 1.0 is true).

Examples

42 == 42           // => true
"abc" == "abc"     // => true
/= other

Test value inequality (negation of ==).

Examples

1 /= 2             // => true
42 /= 42           // => false
=:= other

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
=/= other

Test strict value inequality (negation of =:=).

Examples

1 =/= 1.0           // => true (strict — different types)
1 =/= 1             // => false
class

Return the class of the receiver.

Examples

42 class            // => Integer
"hello" class       // => String
doesNotUnderstand: selector args: arguments

Handle messages the receiver does not understand. Override for custom dispatch.

Examples

42 unknownMessage   // => ERROR: does_not_understand
perform: selector withArguments: arguments

Send a message dynamically with an arguments list.

Examples

42 perform: #abs withArguments: #()   // => 42
performLocally: selector withArguments: arguments

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)
perform: selector withArguments: arguments timeout: timeoutMs

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