Explicit Outcome

Typed outcomes for explicit, predictable Dart development.
explicit_outcome provides Result and Option types — sealed, pattern-matchable containers that make success, failure, presence, and absence first-class citizens in your type signatures.
Installation
dart pub add explicit_outcome
Most users should install explicit instead, which re-exports these types and adds ergonomic utilities. Use explicit_outcome directly only when you need the low-level outcome types without additional utilities.
Result: Res<T, E>
Model operations that can succeed with a value or fail with an error — without throwing exceptions.
import 'package:explicit_outcome/explicit_outcome.dart';
Res<int, String> parsePort(String raw) {
final value = int.tryParse(raw);
if (value == null || value < 1 || value > 65535) {
return Err('invalid port: $raw');
}
return Ok(value);
}
Creating results
| Constructor |
Purpose |
Ok<T, E>(value) |
Wrap a success value. |
Err<T, E>(error) |
Wrap an error value. |
Res<T, E> |
Compact typedef for Result<T, E>. |
Success<T, E> |
Compatibility alias for Ok. |
Failure<T, E> |
Compatibility alias for Err. |
Consuming results
final result = parsePort('8080');
// Branch and return a value:
final message = result.fold(
onSuccess: (port) => 'port=$port',
onError: (error) => 'error=$error',
);
// Branch for side effects:
result.when(
onSuccess: (port) => print('port=$port'),
onError: (error) => print('error=$error'),
);
// Provide a default on error:
final port = result.getOrElse((error) => 8080);
// Pattern match:
switch (result) {
case Ok(:final value):
print('success: $value');
case Err(:final error):
print('error: $error');
}
Composing results
final pipeline = parsePort('8080')
.map((port) => 'http://localhost:$port') // transform success
.mapError((e) => 'ConfigError: $e') // transform error
.next((url) => checkUrl(url)) // chain another Result operation
.or((error) => Ok('http://localhost:8080')); // recover from error
| Method |
Signature |
Behavior |
map |
Res<R, E> map<R>(R Function(T)) |
Transform the success value. Short-circuits on Err. |
mapError |
Res<T, R> mapError<R>(R Function(E)) |
Transform the error value. Short-circuits on Ok. |
next |
Res<R, E> next<R>(Res<R, E> Function(T)) |
Chain another result-returning operation. Short-circuits on Err. |
or |
Res<T, E> or(Res<T, E> Function(E)) |
Recover from error with an alternative result. |
fold |
R fold<R>({R Function(T), R Function(E)}) |
Branch on variant and return a value. |
when |
void when({void Function(T), void Function(E)}) |
Branch for side effects. |
getOrElse |
T getOrElse(T Function(E)) |
Return value or compute a default from the error. |
isSuccess |
bool |
true if Ok. |
isFailure |
bool |
true if Err. |
Option: Opt<T>
Model presence and absence without null.
Opt<int> findIndex(List<String> items, String target) {
final index = items.indexOf(target);
if (index == -1) return Nil();
return Val(index);
}
Creating options
| Constructor |
Purpose |
Val<T>(value) |
Wrap a present value. |
Nil<T>() |
Represent absence. |
Opt<T> |
Compact typedef for Option<T>. |
Consuming options
final option = findIndex(names, 'Alice');
final label = option.fold(
onVal: (i) => 'Found at $i',
onNil: () => 'Not found',
);
option.when(
onVal: (i) => print('index=$i'),
onNil: () => print('absent'),
);
final index = option.getOrElse(() => -1);
Composing options
final result = findIndex(names, 'Alice')
.map((i) => names[i].toUpperCase())
.next((name) => validateName(name))
.or(() => Val('DEFAULT'));
| Method |
Signature |
Behavior |
map |
Opt<R> map<R>(R Function(T)) |
Transform the present value. Propagates Nil. |
next |
Opt<R> next<R>(Opt<R> Function(T)) |
Chain another option-returning operation. Propagates Nil. |
or |
Opt<T> or(Opt<T> Function()) |
Provide an alternative when Nil. |
fold |
R fold<R>({R Function(T), R Function()}) |
Branch on variant and return a value. |
when |
void when({void Function(T), void Function()}) |
Branch for side effects. |
getOrElse |
T getOrElse(T Function()) |
Return value or compute a default. |
hasValue |
bool |
true if Val. |
isNil |
bool |
true if Nil. |
Async: AsyncRes<T, E> and AsyncOpt<T>
Lazy asynchronous wrappers. Work does not start until run() is called.
final pipeline = AsyncRes<int, String>(() async => Ok(2))
.map((v) => v + 3)
.next((v) => AsyncRes(() async => Ok(v * 10)));
final result = await pipeline.run(); // Res<int, String>
Semantics
| Behavior |
Detail |
| Lazy |
No work happens until run() is called. |
| Short-circuit |
After Err/Nil, downstream steps are not invoked. |
| No caching |
Each run() re-executes the pipeline. |
| No retry |
Failures are not retried. |
| No catching |
Thrown exceptions propagate; use fold for expected failures. |
AsyncRes methods
| Method |
Description |
run() |
Execute and return Future<Res<T, E>>. |
map |
Transform the success value (lazy). |
next |
Chain another AsyncRes operation (lazy). |
mapError |
Transform the error value (lazy). |
or |
Recover from error (lazy). |
AsyncOpt methods
| Method |
Description |
run() |
Execute and return Future<Opt<T>>. |
map |
Transform the present value (lazy). |
next |
Chain another AsyncOpt operation (lazy). |
or |
Provide an alternative when Nil (lazy). |
fold |
Await and branch (convenience extension). |
when |
Await and side-effect (convenience extension). |
getOrElse |
Await and default (convenience extension). |
hasValue |
Await and check for Val (convenience extension). |
isNil |
Await and check for Nil (convenience extension). |
ResultAsync<T, E> and OptionAsync<T> are convenience typedefs for Future<Res<T, E>> and Future<Opt<T>> respectively.
API Summary
| Symbol |
Kind |
Description |
Res<T, E> |
typedef |
Compact alias for Result<T, E>. |
Result<T, E> |
sealed class |
Base type for success/error. |
Ok<T, E> |
class |
Success variant. |
Err<T, E> |
class |
Error variant. |
Opt<T> |
typedef |
Compact alias for Option<T>. |
Option<T> |
sealed class |
Base type for present/absent. |
Val<T> |
class |
Present option variant. |
Nil<T> |
class |
Absent option variant. |
ResultAsync<T, E> |
typedef |
Future<Res<T, E>>. |
AsyncRes<T, E> |
class |
Lazy async result. |
OptionAsync<T> |
typedef |
Future<Opt<T>>. |
AsyncOpt<T> |
class |
Lazy async option. |
Success<T, E> |
typedef |
Compatibility alias for Ok. |
Failure<T, E> |
typedef |
Compatibility alias for Err. |
Compatibility