Effect.void
Much like Context.empty
(22)
or Layer.empty
(34)
, Effect.void
is an “empty” Effect that does nothing. On its own, it is not very useful.
// ┌─── Effect.Effect<void>
// ▼
const toVoid = Effect.succeed(3).pipe(
Effect.flatMap((x: number) => Effect.void)
);
// ┌─── Effect.Effect<number>
// ▼
const fromVoid = Effect.void.pipe(
Effect.flatMap((x: void) => Effect.succeed(3))
);
There are two main situations where Effect.void
can be very useful. The first is as a “base” or “fallback” value for some recursive operation, like we saw with Context.empty
(22)
. For example, here is a combinator that runs many effects in sequence, stopping after the first failure.
const allSucceed = <E>(
...effects: ReadonlyArray<Effect.Effect<void, E>>
): Effect.Effect<void, E> =>
effects.reduce((acc, curr) => Effect.zipRight(acc, curr), Effect.void);
// ┌─── Effect.Effect<void, string, never>
// ▼
const nineThousand = allSucceed(
Effect.succeed(3000),
Effect.succeed(6000),
Effect.succeed(9000)
);
const fails = allSucceed(
Effect.succeed(1),
Effect.fail("uh oh"),
Effect.fail("never runs")
);
This is fairly rare in practice, as other default values might be more useful. For example, you might prefer Effect.succeed(undefined)
, or more commonly Effect.succeedNone
, which we will meet in a future lesson.
The more common usage of Effect.void
is as a placeholder. For example, I have this set up in a snippet:
snippet eg
Effect.gen(function* () {
yield* Effect.void
$1
})
endsnippet
This helps me start writing Effect generator logic without immediately seeing red squigglies from an over-eager “generator does not use yield” linting rule.
We can implement Effect.void
as a simple constant.
import { Effect } from "effect";
type Void = Effect.Effect<void>;
const void_: Void = Effect.succeed(undefined);