effect-patterns-core-concepts | Skill Performance & Reviews | TopRankSkills

TopRank Skills

Home / Skills / tools / effect-patterns-core-concepts

effect-patterns-core-concepts

maintained by PaulJPhilp

star 623 account_tree 20 verified_user MIT License
bolt View GitHub

name: effect-patterns-core-concepts description: Effect-TS patterns for Core Concepts. Use when working with core concepts in Effect-TS applications.

Effect-TS Patterns: Core Concepts

This skill provides 49 curated Effect-TS patterns for core concepts. Use this skill when working on tasks related to:

  • core concepts
  • Best practices in Effect-TS applications
  • Real-world patterns and solutions

🟢 Beginner Patterns

Combining Values with zip

Rule: Use zip to run two computations and combine their results into a tuple, preserving error and context handling.

Good Example:

import { Effect, Either, Option, Stream } from "effect";

// Effect: Combine two effects and get both results
const effectA = Effect.succeed(1);
const effectB = Effect.succeed("hello");
const zippedEffect = effectA.pipe(Effect.zip(effectB)); // Effect<[number, string]>

// Option: Combine two options, only Some if both are Some
const optionA = Option.some(1);
const optionB = Option.some("hello");
const zippedOption = Option.all([optionA, optionB]); // Option<[number, string]>

// Either: Combine two eithers, only Right if both are Right
const eitherA = Either.right(1);
const eitherB = Either.right("hello");
const zippedEither = Either.all([eitherA, eitherB]); // Either<never, [number, string]>

// Stream: Pair up values from two streams
const streamA = Stream.fromIterable([1, 2, 3]);
const streamB = Stream.fromIterable(["a", "b", "c"]);
const zippedStream = streamA.pipe(Stream.zip(streamB)); // Stream<[number, string]>

Explanation:
zip runs both computations and pairs their results.
If either computation fails (or is None/Left/empty), the result is a failure (or None/Left/empty).

Anti-Pattern:

Manually running two computations, extracting their results, and pairing them outside the combinator world.
This breaks composability, loses error/context handling, and can lead to subtle bugs.

Rationale:

Use the zip combinator to combine two computations, pairing their results together.
This works for Effect, Stream, Option, and Either, and is useful when you want to run two computations and work with both results.

zip lets you compose computations that are independent but whose results you want to use together.
It preserves error handling and context, and keeps your code declarative and type-safe.


Creating from Synchronous and Callback Code

Rule: Use sync and async to create Effects from synchronous or callback-based computations, making them composable and type-safe.

Good Example:

import { Effect } from "effect";

// Synchronous: Wrap a computation that is guaranteed not to throw
const effectSync = Effect.sync(() => Math.random()); // Effect<never, number, never>

// Callback-based: Wrap a Node.js-style callback API
function legacyReadFile(
  path: string,
  cb: (err: Error | null, data?: string) => void
) {
  setTimeout(() => cb(null, "file contents"), 10);
}

const effectAsync = Effect.async<string, Error>((resume) => {
  legacyReadFile("file.txt", (err, data) => {
    if (err) resume(Effect.fail(err));
    else resume(Effect.succeed(data!));
  });
}); // Effect<string, Error, never>

Explanation:

  • Effect.sync is for synchronous computations that are guaranteed not to throw.
  • Effect.async is for integrating callback-based APIs, converting them into Effects.

Anti-Pattern:

Directly calling synchronous or callback-based APIs inside Effects without lifting them, which can break composability and error handling.

Rationale:

Use the sync and async constructors to lift synchronous or callback-based computations into the Effect world.
This enables safe, composable interop with legacy or third-party code that doesn't use Promises or Effects.

Many APIs are synchronous or use callbacks instead of Promises.
By lifting them into Effects, you gain access to all of Effect's combinators, error handling, and resource safety.


Model Optional Values Safely with Option

Rule: Use Option to model values that may be present or absent, making absence explicit and type-safe.

Good Example:

import { Option } from "effect";

// Create an Option from a value
const someValue = Option.some(42); // Option<number>
const noValue = Option.none(); // Option<never>

// Safely convert a nullable value to Option
const fromNullable = Option.fromNullable(Math.random() > 0.5 ? "hello" : null); // Option<string>

// Pattern match on Option
const result = someValue.pipe(
  Option.match({
    onNone: () => "No value",
    onSome: (n) => `Value: ${n}`,
  })
); // string

// Use Option in a workflow
function findUser(id: number): Option.Option<{ id: number; name: string }> {
  return id === 1 ? Option.some({ id, name: "Alice" }) : Option.none();
}

Explanation:

  • Option.some(value) represents a present value.
  • Option.none() represents absence.
  • Option.fromNullable safely lifts nulla

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!

Skill Details

GitHub Stars 623
GitHub Forks 20
Created Jan 2026
Last Updated 5个月前
tools tools architecture patterns

Related Skills

dagger-design-proposals
chevron_right
nestjs-expert
chevron_right
docker-expert
chevron_right
kafka-streams-topology
chevron_right
kafka-architecture
chevron_right

Build your own?

Join 12,000+ developers contributing to the Claude ecosystem.