Skip to content

2.2.0 - Aug 27, 2026

Choose a tag to compare

@hoc081098 hoc081098 released this 27 Aug 08:57
· 29 commits to master since this release
Immutable release. Only release title and notes can be modified.

https://pub.dev/packages/dart_either/versions/2.2.0

Either

  • Side-effect hooks
    • Added onLeft and onRight; each runs an action on one side and returns
      the original Either.
    • Deprecated aliases remain: tapLeft → onLeft, tap → onRight.
  • Right-side predicate
    • Added isRightAnd to match a Right value with a predicate.
    • exists remains as a deprecated alias.
  • Nullable extraction
    • Added getOrNull for Right and leftOrNull for Left.
    • orNull remains as a deprecated alias of getOrNull.
  • Fallback values
    • Added eager getOrDefault(value).
    • Deprecated lazy getOrElse(() => value); use getOrDefault for an eager
      fallback or getOrHandle((left) => value) for a lazy, left-aware fallback.
  • Composition
    • combine: combine matching sides; otherwise return the sole Left.
    • flatten: convert Either<L, Either<L, R>> to Either<L, R>.
    • merge: extract the value from Either<T, T>.

EitherEffect, Either.binding, and Either.futureBinding

  • Direct short-circuit
    • Added effect.raise(left) to exit the owning scope with Left(left).
    • It avoids an intermediate Left and returns Never, so it works in
      expressions such as nullable ?? effect.raise('missing').
    • ensure and ensureNotNull now delegate their short-circuit paths to
      raise.
  • 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.
    • bind moved from an instance member to BindEitherEffectExtension.
      Standard unprefixed imports keep effect.bind(either) unchanged. Prefixed
      imports must use the extension override; selective imports must include the
      extension.
  • 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 StateError instead of producing Right.

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 EitherEffect construction;
    • 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.

Full Changelog: 2.1.0...2.2.0