avoid_either_of_future
v1.0.0 Warning fpdart
This rule flags a Future nested inside the synchronous Either or Option — either written as a type (Either<Failure, Future<League>>) or produced by mapping with an async function.
Why use this rule
Section titled “Why use this rule”Either and Option are synchronous wrappers. Mapping one with a function that returns a Future does not make the pipeline async — it makes an Either<L, Future<R>>.
That type is a trap. The future is created and starts running immediately, but it sits inside the success channel, so the error channel no longer covers it. A rejection becomes an unhandled async error rather than a Left, and callers receive a Right holding a future that may already have failed. Every fold written downstream still reports success.
The pipeline has to enter the async world at that point, which is exactly what toTaskEither() is for.
See also: fpdart: TaskEither, From sync to async functional programming
Either<Failure, Future<League>> save(LeagueDraft draft) => validate(draft).map((valid) => api.saveLeague(valid));Convert once, early, then keep chaining in the async world:
TaskEither<Failure, League> save(LeagueDraft draft) => validate(draft).toTaskEither().flatMap( (valid) => TaskEither.tryCatch( () => api.saveLeague(valid), (e, s) => Failure.from(e), ), );chainEither is the counterpart when a synchronous validation step joins an already-async pipeline.
Known limitations
Section titled “Known limitations”TaskEither, TaskOption and the other async wrappers are unaffected — a Future belongs inside them.
An Iterable.map returning futures is ordinary Dart and is never reported; only fpdart’s synchronous wrappers are.
This rule is about a Future nested inside Either. The reverse nesting, Future<Either<L, R>>, is a different matter — it is correct, merely not idiomatic — and is covered by avoid_future_of_either.
Configuration
Section titled “Configuration”This rule is in the recommended preset, so it is on with
preset: recommended or preset: opinionated. Add it to preset: core with
avoid_either_of_future: true.
To turn it off:
rules: avoid_either_of_future: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_future_of_either— Future<Either> throws away the composition TaskEither already gives you.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.