Skip to content

Instantly share code, notes, and snippets.

@ChristianAlexander
Last active July 6, 2026 01:18
Show Gist options
  • Select an option

  • Save ChristianAlexander/7ac816432e3146734404ce7daf16f57b to your computer and use it in GitHub Desktop.

Select an option

Save ChristianAlexander/7ac816432e3146734404ce7daf16f57b to your computer and use it in GitHub Desktop.
Intro to Effect

Intro to Effect

Companion gist to a YouTube video describing how the Effect TypeScript library works.

Watch the video

import { Effect, Data, Schedule, Context, Layer } from "effect";
class SiteUnreachable extends Data.TaggedError("SiteUnreachable")<{
readonly url: string;
readonly cause: unknown;
}> {}
type CheckResult = {
readonly ok: boolean;
readonly status?: number;
};
class Checker extends Context.Tag("Checker")<
Checker,
{
readonly check: (
url: string,
) => Effect.Effect<CheckResult, SiteUnreachable>;
}
>() {}
const check = (url: string) =>
Effect.gen(function* () {
const checker = yield* Checker;
const res = yield* checker.check(url);
yield* Effect.log(`${url} responded with ${res.status}`);
return res.ok;
});
const retryPolicy = Schedule.exponential("100 millis").pipe(
Schedule.jittered, // randomize each delay ±20%
Schedule.intersect(Schedule.recurs(4)), // AND: at most 4 retries
Schedule.upTo("10 seconds"), // AND: give up after 10s total
);
const withRetries = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
effect.pipe(
Effect.tapError((e) => Effect.logError(e)),
Effect.retry(retryPolicy),
);
const CheckerLive = Layer.succeed(Checker, {
check: (url) =>
Effect.tryPromise({
try: (signal) => fetch(url, { signal }),
catch: (cause) => new SiteUnreachable({ url, cause }),
}),
});
const CheckerTest = Layer.succeed(Checker, {
check: (url) => {
if (url.includes("/2")) {
return Effect.fail(new SiteUnreachable({ url, cause: "failure" }));
}
return Effect.succeed({ ok: true, status: 200 });
},
});
const FiveTimeFailureCheckerTest = Layer.effect(
Checker,
Effect.gen(function* () {
let failuresRemaining = 5;
return Checker.of({
check: (url) => {
if (failuresRemaining > 0) {
failuresRemaining--;
return Effect.fail(new SiteUnreachable({ url, cause: "failure" }));
}
return Effect.succeed({ ok: true, status: 200 });
},
});
}),
);
async function main() {
const urls = [
"https://example.com",
"https://example.com/1",
"https://example.com/2",
"not-a-url://fail.now",
"https://example.com/3",
];
const checks = urls.map((url) =>
check(url).pipe(
withRetries,
Effect.catchTag("SiteUnreachable", (e) => Effect.succeed(false)),
),
);
const run = Effect.all(checks, { concurrency: 2 }).pipe(
Effect.provide(FiveTimeFailureCheckerTest),
);
const result = await Effect.runPromise(run);
console.log(result);
}
main();
function* countTo(target) {
for (let i = 0; i <= target; i++) {
yield i;
}
}
const countToTwo = countTo(2);
console.log(countToTwo.next()); // { value: 0, done: false }
console.log(countToTwo.next()); // { value: 1, done: false }
console.log(countToTwo.next()); // { value: 2, done: false }
console.log(countToTwo.next()); // { value: undefined, done: true }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment