explicit_outcome 0.0.2 copy "explicit_outcome: ^0.0.2" to clipboard
explicit_outcome: ^0.0.2 copied to clipboard

Dart typed outcomes for explicit, predictable development.

Explicit Outcome #

style: very good analysis License: MPL-2.0 Dart 3.6+

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 #

2
likes
160
points
51
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Dart typed outcomes for explicit, predictable development.

Homepage
Repository (GitHub)
View/report issues

License

MPL-2.0 (license)

Dependencies

meta

More

Packages that depend on explicit_outcome