Result
Result — Value type for expected success/failure outcomes (ADR 0060).
Result is a sealed value class representing either a successful outcome
(ok:) or a failure (error:). Use Result for operations where failure
is a normal, expected case — file I/O, parsing, network requests. Use
exceptions (on:do:) for programming errors like wrong argument types.
Constructors
Result ok: 42 // => a successful Result holding 42
Result error: #file_not_found // => a failed Result
Result tryDo: [expr] // => wraps exception-raising code
Combinators
(Result ok: 42) map: [:v | v + 1] // => Result ok: 43
(Result ok: 42) andThen: [:v | Result ok: v * 2] // => Result ok: 84
(Result error: #nope) map: [:v | v + 1] // => Result error: #nope
(Result ok: 42) valueOr: 0 // => 42
(Result error: #nope) valueOr: 0 // => 0
(Result ok: 42) ifOk: [:v | v] ifError: [:e | 0] // => 42
When to Use Result vs Exceptions
| Failure mode | Use |
|---|---|
| File not found, parse error | Result |
| does_not_understand, arity error | Exception |
| User input validation | Result |
| Actor lifecycle errors | Exception |
@see Exception (for exception-based error handling) @see Error (for exception subclass with reason codes)
Methods
Class Methods
Create a successful Result wrapping a value (class method).
Examples
Result ok: 42 // => Result ok: 42
Result ok: "hello" // => Result ok: "hello"
Result ok: nil // => Result ok: nil
Create a failed Result wrapping an error reason (class method).
The reason should be a structured error (Exception subclass instance or
#beamtalk_error{}). Bare symbols are accepted for convenience but FFI
wrappers must provide structured errors at public boundaries.
Examples
Result error: #not_found // => Result error: #not_found
Result error: Error new // => Result error: <Error>
To capture a raised exception as a Result, use Result tryDo: [Error signal: "oops"].
Evaluate a block and return a Result (class method).
If the block succeeds, returns Result ok: value.
If the block raises an exception, returns Result error: theException.
Bridges between exception-raising legacy code and Result combinators.
Examples
Result tryDo: [42] // => Result ok: 42
Result tryDo: [Exception signal: "oops"] // => Result error: <Exception>
Result tryDo: [(File readAll: "missing.txt") unwrap] // => Result error: <Exception>
Convert an Erlang-style tagged tuple to a Result (class method).
Accepts {ok, V} and {error, R} tuples from Erlang messages or
other non-FFI sources. Raises on tuples that are not ok/error tagged.
Examples
Result fromTuple: (Tuple withAll: #(#ok, 42)) // => Result ok: 42
Result fromTuple: (Tuple withAll: #(#error, #oops)) // => Result error: ...
Instance Methods
True if this Result holds a success value.
Examples
(Result ok: 42) ok // => true
(Result error: #x) ok // => false
True if this Result holds an error.
Examples
(Result ok: 42) isError // => false
(Result error: #x) isError // => true
The success value. Raises if this is an error Result.
Prefer valueOr:, valueOrDo:, or ifOk:ifError: for safe access.
Examples
(Result ok: 42) value // => 42
(Result error: #x) value // => Exception: Cannot access 'value'...
The error reason. Raises if this is an ok Result.
Prefer ifOk:ifError: or mapError: for safe access.
Examples
(Result error: #x) error // => #x
(Result ok: 42) error // => Exception: Cannot access 'error'...
Unwrap the success value, or return the default if error.
Examples
(Result ok: 42) valueOr: 0 // => 42
(Result error: #x) valueOr: 0 // => 0
Unwrap the success value, or evaluate a block with the error reason.
Examples
(Result ok: 42) valueOrDo: [:e | 0] // => 42
(Result error: #x) valueOrDo: [:e | -1] // => -1
Unwrap the success value, or raise an exception.
Re-raises the errReason directly if it is an Exception (preserving
class, message, and hints). For raw values, signals a generic Error.
Examples
(Result ok: 42) unwrap // => 42
(Result error: #x) unwrap // => Exception: unwrap called on Result error: #x
Apply a block to the success value, wrapping result in a new ok Result. If this is an error, returns self unchanged.
Examples
(Result ok: 42) map: [:v | v + 1] // => Result ok: 43
(Result error: #x) map: [:v | v + 1] // => Result error: #x
Apply a block that returns a Result. Flattens the nesting. If this is an error, returns self unchanged.
Examples
(Result ok: 42) andThen: [:v | Result ok: v * 2] // => Result ok: 84
(Result error: #x) andThen: [:v | Result ok: v] // => Result error: #x
Apply a block to the error reason, wrapping result in a new error Result. If this is ok, returns self unchanged.
Examples
(Result error: #x) mapError: [:e | "wrapped: " ++ e printString]
// => Result error: "wrapped: #x"
(Result ok: 42) mapError: [:e | "oops"] // => Result ok: 42
Evaluate a block with the error reason if this is an error; return self if ok.
Use at the end of an andThen:/map: chain as a shared terminal error handler.
The error case is handled by the block; the ok case is passed through unchanged.
Examples
(Result ok: 42) ifError: [:e | 0] // => Result ok: 42
(Result error: #oops) ifError: [:e | e] // => #oops
chain := (Result ok: 1) andThen: [:v | Result error: #fail].
chain ifError: [:_ | "handled"] // => "handled"
Handle both ok and error cases with blocks.
Examples
(Result ok: 42) ifOk: [:v | v + 1] ifError: [:e | -1] // => 43
(Result error: #x) ifOk: [:v | v] ifError: [:e | 0] // => 0
Human-readable representation: "Result ok: value" or "Result error: reason".
Examples
(Result ok: 42) printString // => "Result ok: 42"
(Result error: #x) printString // => "Result error: #x"
Inherited Methods
From Value
Return a developer-readable string representation showing fields.
Produces ClassName(field: value, ...). Field values are recursively
inspected — strings are quoted, nested objects show their own inspect.
A class with no fields produces ClassName().
Examples
ValuePoint x: 3 y: 4 inspect // => "ValuePoint(x: 3, y: 4)"
ValuePoint new inspect // => "ValuePoint(x: 0, y: 0)"
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