Subprocess
Subprocess — Actor for interactive bidirectional communication with OS processes.
Backed by beamtalk_subprocess.erl (hand-written gen_server). State — port,
stdout/stderr queues, exitCode, portClosed flag — lives entirely in the Erlang
gen_server; there are no Beamtalk-level state: declarations.
Basic usage
agent := (Subprocess open: "echo" args: #("hello")) unwrap
line := agent readLine. // => "hello"
agent exitCode. // => 0
agent close.
Environment and working directory
agent := Subprocess open: "make" args: #("test")
env: #{#"CI" => #"true"}
dir: #"/path/to/project"
Streaming stdout
agent lines do: [:line | Transcript show: line]
@see ReactiveSubprocess (for push-mode async delivery) @see System (for simple one-shot commands via osCmd:)
Methods
Class Methods
Convenience factory — open a subprocess with command and args.
Returns a Result wrapping the Subprocess actor on success, or an error if the process could not be started (e.g. binary not found).
Examples
agent := (Subprocess open: "ls" args: #("-la")) unwrap
Convenience factory — open a subprocess with command, args, and working directory.
Examples
agent := (Subprocess open: "ls" args: #("-la") dir: #"/tmp") unwrap
Convenience factory — open a subprocess with command, args, environment, and working directory.
Examples
agent := (Subprocess open: "make" args: #("test") env: #{#"CI" => #"true"} dir: #"/tmp") unwrap
Instance Methods
Write a line to the subprocess's stdin (appends newline).
Examples
agent writeLine: "{\"jsonrpc\":\"2.0\",\"method\":\"ping\"}"
Read one line from stdout. Blocks until a line is available. Returns nil at EOF.
Examples
line := agent readLine. // => "hello" or nil
Read one line from stdout with a timeout in milliseconds. Returns nil on timeout or EOF.
Examples
line := agent readLine: 5000. // => String or nil
Read one line from stderr. Blocks until a line is available. Returns nil at EOF.
Examples
errLine := agent readStderrLine.
Read one line from stderr with a timeout in milliseconds. Returns nil on timeout or EOF.
Examples
errLine := agent readStderrLine: 5000.
Return a Stream of stdout lines. Each step sends a sync message to the actor.
Examples
agent lines do: [:line | Transcript show: line]
Return a Stream of stderr lines. Same mechanics as lines.
Examples
agent stderrLines do: [:line | Transcript show: line]
Get the exit code. Returns nil if the subprocess is still running.
Examples
agent exitCode. // => 0
Force-close the subprocess (sends kill to process group).
Examples
agent close.
Inherited Methods
From Actor
Wrap this actor with a custom message timeout.
Returns a TimeoutProxy that forwards all messages to this actor using
the given timeout (in milliseconds, 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
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 during
graceful shutdown (stop). The reason parameter is a Symbol indicating
why the actor is stopping (e.g., #normal for graceful stop).
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:.
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.
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
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
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 a developer-readable string representation.
Default implementation returns "a ClassName". Subclasses such as
Integer, String, and List override this to return richer output.
Examples
42 printString // => "42"
Return a user-facing string representation for display purposes.
Default implementation delegates to printString. Subclasses such as
String and Symbol override this to return a more readable form without
developer annotations (e.g. no surrounding quotes or # prefix).
Examples
42 displayString // => "42"
Inspect the receiver.
Examples
42 inspect // => "42"
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.
Examples
42 isKindOf: Integer // => true
42 isKindOf: Object // => true
#foo isKindOf: Symbol // => true
#foo isKindOf: String // => false
Raise an error with the given message.
Examples
self error: "something went wrong"
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