Skip to content

prefer_and_then

v1.2.0 Warning Fix fpdart

This rule flags flatMap whose callback never reads the value it is handed, which is what andThen expresses.

fpdart declares andThen as literally flatMap((_) => then()), so this is not a behaviour change — it is the same call under the name that says what it does.

The cost of the long form is what a reader has to do to understand a pipeline. Every flatMap in a chain raises the question “does this step use the previous result?”, and the only way to answer it is to open the callback and look for the parameter. andThen answers it in the name, which is why the same two-line shape keeps reappearing across unrelated features once someone notices it.

Only the provably-ignored case is reported. A callback that reads its parameter has a real dependency on the previous step, and andThen throws that value away.

See also: fpdart andThen

TaskEither<String, Unit> clearSession() => TaskEither.of(unit);
TaskEither<String, Unit> logout() => TaskEither.of(unit);
// The callback ignores its argument, so the name says less than it could.
TaskEither<String, Unit> reset() => clearSession().flatMap((_) => logout());
// A named parameter nothing reads is the same situation.
TaskEither<String, Unit> resetNamed() =>
clearSession().flatMap((value) => logout());
TaskEither<String, Unit> reset() => clearSession().andThen(logout);

A callback that uses its parameter is a real flatMap, and converting it would discard a value the next step depends on:

TaskEither<String, int> parse(String raw) => TaskEither.of(raw.length);
TaskEither<String, int> parsed(TaskEither<String, String> pipeline) =>
pipeline.flatMap((value) => parse(value));

A block-bodied callback is a real function and is left alone:

TaskEither<String, Unit> reset() => clearSession().flatMap((_) {
return logout();
});

An unrelated class with a flatMap method is never reported. The rule resolves the receiver’s type rather than matching the name.

This rule is in the opinionated preset. With a lower preset, enable it by name with prefer_and_then: true.

To turn it off:

many_lints.yaml
rules:
prefer_and_then: false

To keep the rule on but skip certain paths, use per-rule exclude.