avoid_future_of_either
This rule flags a function or method returning Future<Either<L, R>>, which TaskEither<L, R> already expresses.
Why use this rule
Section titled “Why use this rule”Future<Either<L, R>> is not a bug — unlike Either<L, Future<R>>, which avoid_either_of_future reports. It is simply the same thing TaskEither is, with the composition thrown away.
A caller cannot flatMap a Future<Either> without awaiting first, so every chain has to leave the fpdart world and come back:
final either = await repo.getUser(id);final result = await either.match( (failure) async => left(failure), (user) => repo.loadOrders(user.id),);In TaskEither the same pipeline is one expression, because the type carries both the asynchrony and the failure channel:
final result = await repo.getUser(id).flatMap( (user) => repo.loadOrders(user.id), ).run();The eagerness matters too. A Future starts running the moment it is created, so a Future<Either> cannot be retried, delayed, or built up now and run later. A TaskEither describes the work instead of performing it.
Future<Either<Failure, User>> getUser(String id) async { return right(await api.get(id));}TaskEither<Failure, User> getUser(String id) => TaskEither.tryCatch( () => api.get(id), (error, stackTrace) => Failure.from(error), );Known limitations
Section titled “Known limitations”FutureOr<Either<L, R>> is not reported: it may complete synchronously, so it is a different shape with a different answer.
A generator (Stream<Either<L, R>> with async*) is never reported — one TaskEither cannot stand in for a stream of values.
The reverse nesting, Either<L, Future<R>>, belongs to avoid_either_of_future; the two rules never report the same line.
Future<Option<T>> is the same argument for the other wrapper and has its own rule, avoid_future_of_option, so a project can enable either half alone.
Related assist
Section titled “Related assist”No quick fix is offered: the conversion changes a public signature, so every call site needs updating and a “apply all” would leave the project uncompilable.
There is an assist. Put the cursor on the function and pick “Convert to TaskEither” — it rewrites the signature and moves the body into a TaskEither. See Assists for what it does with call sites.
Options
Section titled “Options”many_lints: rules: avoid_future_of_either: ignore_private: truerules: avoid_future_of_either: ignore_private: true| Option | Type | Default | Description |
|---|---|---|---|
ignore_private |
bool | false |
Skip private functions and methods |
The default is false because a Future<Either> is awkward to consume wherever it appears — unlike a try/catch, which really is an implementation detail.
Configuration
Section titled “Configuration”This rule is in the opinionated preset. With a lower preset, enable it by
name with avoid_future_of_either: true.
To turn it off:
rules: avoid_future_of_either: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_either_of_future— A Future nested in Either or Option escapes the error channel.avoid_future_of_option— Future<Option> throws away the composition TaskOption already gives you.prefer_task_either_over_try_catch— A repository’s failures belong in its signature, not in a try/catch.prefer_chain_either— chainEither lifts a synchronous Either step for you.