dart_either 2.4.0
dart_either: ^2.4.0 copied to clipboard
Either monad for Dart language and Flutter framework. Type-safe error handling, railway oriented programming. Supports Monad comprehensions, async map, async flatMap.
2.4.0 Sep 06, 2026 #
Either operations #
-
Relocated
flatMap,getOrElse,getOrHandle,handleError, andhandleErrorWithfromEitherinstance members to exported generic extensions.- Statically typed dot-call syntax and behavior are unchanged, while analyzer-valid widened receivers no longer fail at a covariant virtual method boundary.
- Selective imports must include the relevant extension type,
and
dynamicreceivers no longer dispatch to these operations.
-
Split every value-operation extension exported by
either_extensions.dartinto a method-named source file. This file split changes only the source layout; public exports, call syntax, and behavior remain unchanged, so no consumer migration is required for the split.
Either.parSequenceN and Either.parTraverseN #
-
Fixed
Either.parSequenceNandEither.parTraverseNwith finite concurrency so functions still waiting for a permit are not invoked after the first observedLeft, thrown error, or failed future.- A
Leftis returned when it is observed first; an ordinary error observed first is propagated with its stack trace. - Already-running functions remain non-cancellable and may finish their side effects.
- A
-
A non-null
maxConcurrentless than or equal to zero now throws anArgumentErrorsynchronously, before inputs are traversed, theparTraverseNmapper is called, or callbacks are invoked. -
Errors thrown while iterating the input or invoking the
parTraverseNmapper now propagate synchronously instead of completing the returned future with an error. No asynchronous operation callback is invoked if this input preparation fails.
2.3.0 Sep 02, 2026 #
-
Added
isLeftAnd, which evaluates a predicate forLeftvalues and returnsfalseforRightvalues. -
Added
tryCatchandtryCatchAsyncas the canonical synchronous and asynchronous error-capture APIs. Both use required namedactionanderrorMapperparameters.tryCatchAsynccaptures errors thrown before a future is returned as well as errors that complete the future.- Deprecated
catchErrorin favor oftryCatch. - Deprecated
catchFutureErrorin favor oftryCatchAsync. - Deprecated
catchStreamErrorin favor ofStream.toEitherStream. - The deprecated aliases retain their existing call syntax throughout
2.x.
- Deprecated
-
Added
registerFatalError<T>()to exclude a registered error type and its subtypes from conversion toLeft. Registered errors retain their original error and stack trace acrosstryCatch,tryCatchAsync,Future.toEitherFuture, andStream.toEitherStream. Registration is per isolate, additive, and idempotent; spawned isolates must register their own fatal types. -
Added
bindingAsyncas the canonical asynchronous counterpart tobinding. DeprecatedfutureBindingin favor ofbindingAsync; the alias retains its existing call syntax and behavior throughout2.x. -
Fixed
handleErrorto preserve the originalRightinstance instead of creating an equivalent one. Callback invocation andLeftrecovery behavior are unchanged. -
Clarified the channel and callback semantics of
handleErrorWith,handleError,redeem, andredeemWith.
2.2.0 Aug 27, 2026 #
Either #
- Side-effect hooks
- Added
onLeftandonRight; each runs an action on one side and returns the originalEither. - Deprecated aliases remain:
tapLeft→onLeft,tap→onRight.
- Added
- Right-side predicate
- Added
isRightAndto match aRightvalue with a predicate. existsremains as a deprecated alias.
- Added
- Nullable extraction
- Added
getOrNullforRightandleftOrNullforLeft. orNullremains as a deprecated alias ofgetOrNull.
- Added
- Fallback values
- Added eager
getOrDefault(value). - Deprecated lazy
getOrElse(() => value); usegetOrDefaultfor an eager fallback orgetOrHandle((left) => value)for a lazy, left-aware fallback.
- Added eager
- Composition
combine: combine matching sides; otherwise return the soleLeft.flatten: convertEither<L, Either<L, R>>toEither<L, R>.merge: extract the value fromEither<T, T>.
EitherEffect, Either.binding, and Either.futureBinding #
- Direct short-circuit
- Added
effect.raise(left)to exit the owning scope withLeft(left). - It avoids an intermediate
Leftand returnsNever, so it works in expressions such asnullable ?? effect.raise('missing'). ensureandensureNotNullnow delegate their short-circuit paths toraise.
- Added
- Variance-safe capability
- Reworked
EitherEffect<L>from a covariant public class into an opaque, contravariant, scope-bound capability. - Unsafe widening that previously compiled is now rejected; safe narrowing is supported.
bindmoved from an instance member toBindEitherEffectExtension. Standard unprefixed imports keepeffect.bind(either)unchanged. Prefixed imports must use the extension override; selective imports must include the extension.
- Reworked
- Scope lifetime
- Capabilities are now revoked when their sync or async binding scope ends.
- Reusing a captured capability afterward throws
StateError. - Swallowing a scope's short-circuit signal and then completing normally now
throws
StateErrorinstead of producingRight.
Import migration for EitherEffect.bind #
The usual unprefixed package import keeps the existing call syntax. With a prefixed import, invoke the named extension explicitly:
import 'package:dart_either/dart_either.dart' as de;
final result = de.Either<String, int>.binding((effect) {
return de.BindEitherEffectExtension(effect).bind(
de.Either<String, int>.right(1),
);
});
For a selective unprefixed import, include BindEitherEffectExtension in the
show list. The Dart SDK constraint remains >=3.0.0 <4.0.0.
Documentation and verification #
- Updated the README, runnable examples, API docs, variance guidance, and binding-scope docs.
- Added regression coverage for:
- new APIs and deprecated aliases;
- covariance widening and rejected external
EitherEffectconstruction; - nested sync/async scopes, capability revocation, and intercepted short-circuits.
- CI now runs the complete suite on every configured Dart SDK and collects stable-SDK coverage.
2.1.0 Mar 07, 2026 #
- Promoted
Either.parSequenceNandEither.parTraverseNfrom experimental to stable. - Added complete API docs and examples for
Either.parSequenceNandEither.parTraverseN. - Added unit tests for
Either.parSequenceNandEither.parTraverseN, including concurrency-limit and short-circuit cases. - Added
@useResultannotations to public APIs that should not be ignored (for example:isLeft,isRight,map,flatMap,swap,exists,all,toEitherStream,left,right, and others).
API migration notes #
Either.parSequenceNchanged from positional parameters to named parameters:// Before (2.0.0) Either.parSequenceN<String, int>(functions, n); // Now (2.1.0) Either.parSequenceN<String, int>( functions: functions, maxConcurrent: n, );Either.parTraverseNchanged from positional parameters to named parameters:// Before (2.0.0) Either.parTraverseN<String, int, int>(values, mapper, n); // Now (2.1.0) Either.parTraverseN<String, int, int>( values: values, mapper: mapper, maxConcurrent: n, );maxConcurrentcontrols concurrency.- Pass a number (for example
2) to limit concurrency. - Pass
nullfor unlimited concurrency.
- Pass a number (for example
2.0.0 Sep 01, 2024 #
-
Require Dart 3.0.0 or higher
>=3.0.0 <4.0.0. -
Make
Eithera sealed class,EitherEffecta sealed class, andControlErrora final class. Now you can use exhaustive switch expressions onEitherinstances.final Either<String, int> either = Either.right(10); // Use the `when` method to handle either.when( ifLeft: (l) => print('Left: $l'), ifRight: (r) => print('Right: $r'), ); // Prints Right: Either.Right(10) // Or use Dart 3.0 switch expression syntax 🤘 print( switch (either) { Left() => 'Left: $either', Right() => 'Right: $either', }, ); // Prints Right: Either.Right(10)
1.0.0 Aug 23, 2022 #
- This is our first stable release.
0.0.1 Apr 27, 2021 #
- Initial version, created by Stagehand