EF

effect-patterns-value-handling

Improve code reliability by using Option types for explicit value handling in Effect-TS.

Install

mkdir -p .claude/skills/effect-patterns-value-handling && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/6078" && unzip -o skill.zip -d .claude/skills/effect-patterns-value-handling && rm skill.zip

Installs to .claude/skills/effect-patterns-value-handling

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Effect-TS patterns for Value Handling. Use when working with value handling in Effect-TS applications.
102 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Replace null or undefined with Option types
  • Perform pattern matching on optional data
  • Transform optional values using map and flatMap
  • Provide fallback values with getOrElse
  • Filter optional values using predicates
  • Combine multiple Options into a single result

How it works

The skill uses the Effect-TS Option module to wrap values in Some or None containers. It forces explicit handling of missing data through pattern matching and functional combinators.

Inputs & outputs

You give it
A nullable value or an Option instance
You get back
A type-safe Option structure or a processed value

When to use effect-patterns-value-handling

  • Refactoring code to replace null with Option
  • Implementing pattern matching for optional data
  • Building type-safe value workflows

About this skill

Effect-TS Patterns: Value Handling

This skill provides 2 curated Effect-TS patterns for value handling. Use this skill when working on tasks related to:

  • value handling
  • Best practices in Effect-TS applications
  • Real-world patterns and solutions

🟡 Intermediate Patterns

Optional Pattern 1: Handling None and Some Values

Rule: Use Option to represent values that may not exist, replacing null/undefined with type-safe Option that forces explicit handling.

Good Example:

This example demonstrates Option handling patterns.

import { Effect, Option } from "effect";

interface User {
  id: string;
  name: string;
  email: string;
}

interface Profile {
  bio: string;
  website?: string;
  location?: string;
}

const program = Effect.gen(function* () {
  console.log(
    `\n[OPTION HANDLING] None/Some values and pattern matching\n`
  );

  // Example 1: Creating Options
  console.log(`[1] Creating Option values:\n`);

  const someValue: Option.Option<string> = Option.some("data");
  const noneValue: Option.Option<string> = Option.none();

  const displayOption = <T,>(opt: Option.Option<T>, label: string) =>
    Effect.gen(function* () {
      if (Option.isSome(opt)) {
        yield* Effect.log(`${label}: Some(${opt.value})`);
      } else {
        yield* Effect.log(`${label}: None`);
      }
    });

  yield* displayOption(someValue, "someValue");
  yield* displayOption(noneValue, "noneValue");

  // Example 2: Creating from nullable values
  console.log(`\n[2] Converting nullable to Option:\n`);

  const possiblyNull = (shouldExist: boolean): string | null =>
    shouldExist ? "found" : null;

  const toOption = (value: string | null | undefined): Option.Option<string> =>
    value ? Option.some(value) : Option.none();

  const opt1 = toOption(possiblyNull(true));
  const opt2 = toOption(possiblyNull(false));

  yield* displayOption(opt1, "toOption(found)");
  yield* displayOption(opt2, "toOption(null)");

  // Example 3: Pattern matching on Option
  console.log(`\n[3] Pattern matching with match():\n`);

  const userId: Option.Option<string> = Option.some("user-123");

  const message = Option.match(userId, {
    onSome: (id) => `User ID: ${id}`,
    onNone: () => "No user found",
  });

  yield* Effect.log(`[MATCH] ${message}`);

  const emptyUserId: Option.Option<string> = Option.none();

  const emptyMessage = Option.match(emptyUserId, {
    onSome: (id) => `User ID: ${id}`,
    onNone: () => "No user found",
  });

  yield* Effect.log(`[MATCH] ${emptyMessage}\n`);

  // Example 4: Transforming with map
  console.log(`[4] Transforming values with map():\n`);

  const userCount: Option.Option<number> = Option.some(42);

  const doubled = Option.map(userCount, (count) => count * 2);

  yield* displayOption(doubled, "doubled");

  // Chaining maps
  const email: Option.Option<string> = Option.some("[email protected]");

  const domain = Option.map(email, (e) =>
    e.split("@")[1] ?? "unknown"
  );

  yield* displayOption(domain, "email domain");

  // Example 5: Chaining with flatMap
  console.log(`\n[5] Chaining operations with flatMap():\n`);

  const findUser = (id: string): Option.Option<User> =>
    id === "user-1"
      ? Option.some({ id, name: "Alice", email: "[email protected]" })
      : Option.none();

  const getProfile = (userId: string): Option.Option<Profile> =>
    userId === "user-1"
      ? Option.some({ bio: "Developer", website: "alice.dev" })
      : Option.none();

  const userId2 = Option.some("user-1");

  // Chained operations: userId -> user -> profile
  const profileChain = Option.flatMap(userId2, (id) =>
    Option.flatMap(findUser(id), (user) =>
      getProfile(user.id)
    )
  );

  const profileResult = Option.match(profileChain, {
    onSome: (profile) => `Bio: ${profile.bio}, Website: ${profile.website}`,
    onNone: () => "No profile found",
  });

  yield* Effect.log(`[CHAIN] ${profileResult}\n`);

  // Example 6: Fallback values with getOrElse
  console.log(`[6] Default values with getOrElse():\n`);

  const optionalStatus: Option.Option<string> = Option.none();

  const status = Option.getOrElse(optionalStatus, () => "unknown");

  yield* Effect.log(`[DEFAULT] Status: ${status}`);

  // Real value
  const knownStatus: Option.Option<string> = Option.some("active");

  const realStatus = Option.getOrElse(knownStatus, () => "unknown");

  yield* Effect.log(`[VALUE] Status: ${realStatus}\n`);

  // Example 7: Filter with predicate
  console.log(`[7] Filtering with conditions:\n`);

  const ageOption: Option.Option<number> = Option.some(25);

  const isAdult = Option.filter(ageOption, (age) => age >= 18);

  yield* displayOption(isAdult, "Adult check (25)");

  const ageOption2: Option.Option<number> = Option.some(15);

  const isAdult2 = Option.filter(ageOption2, (age) => age >= 18);

  yield* displayOption(isAdult2, "Adult check (15)");

  // Example 8: Multiple Options (all present?)
  console.log(`\n[8] Combining multiple Options:\n`);

  const firstName: Option.Option<string> = Option.some("John");
  const lastName: Option.Option<string> = Option.some("Doe");
  const middleName: Option.Option<string> = Option.none();

  // All three present?
  const allPresent = Option.all([firstName, lastName, middleName]);

  yield* displayOption(allPresent, "All present");

  // Just two
  const twoPresent = Option.all([firstName, lastName]);

  yield* displayOption(twoPresent, "Two present");

  // Example 9: Converting Option to Error
  console.log(`\n[9] Converting Option to Result/Error:\n`);

  const optionalConfig: Option.Option<{ apiKey: string }> = Option.none();

  const configOrError = Option.match(optionalConfig, {
    onSome: (config) => config,
    onNone: () => {
      throw new Error("Configuration not found");
    },
  });

  // In real code, would catch error
  const result = Option.match(optionalConfig, {
    onSome: (config) => ({ success: true, value: config }),
    onNone: () => ({ success: false, error: "config-not-found" }),
  });

  yield* Effect.log(`[CONVERT] ${JSON.stringify(result)}\n`);

  // Example 10: Option in business logic
  console.log(`[10] Practical: Optional user settings:\n`);

  const userSettings: Option.Option<{
    theme: string;
    notifications: boolean;
  }> = Option.some({
    theme: "dark",
    notifications: true,
  });

  const getTheme = Option.map(userSettings, (s) => s.theme);
  const theme = Option.getOrElse(getTheme, () => "light"); // Default

  yield* Effect.log(`[SETTING] Theme: ${theme}`);

  // No settings
  const noSettings: Option.Option<{ theme: string; notifications: boolean }> =
    Option.none();

  const noTheme = Option.map(noSettings, (s) => s.theme);
  const defaultTheme = Option.getOrElse(noTheme, () => "light");

  yield* Effect.log(`[DEFAULT] Theme: ${defaultTheme}`);
});

Effect.runPromise(program);

Rationale:

Option enables null-safe programming:

  • Some(value): Value exists
  • None: Value doesn't exist
  • Pattern matching: Handle both cases
  • Chaining: Compose operations safely
  • Fallbacks: Default values
  • Conversions: Option ↔ Error

Pattern: Use Option.isSome(), Option.isNone(), match(), map(), flatMap()


Null/undefined causes widespread bugs:

Problem 1: Billion-dollar mistake

  • Tony Hoare invented null in ALGOL in 1965
  • Created "billion-dollar mistake"
  • 90% of security vulnerabilities involve null handling

Problem 2: Undefined behavior

  • user.profile.name - any property could be null
  • Runtime error: "Cannot read property 'name' of undefined"
  • No compile-time warning
  • Production crash

Problem 3: Silent failures

  • Function returns null on failure
  • Caller doesn't check
  • Uses null as if it's a value
  • Corrupts state downstream

Problem 4: Conditional hell

if (user !== null && user.profile !== null && user.profile.name !== null) {
  // Do thing
}

Solutions:

Option type:

  • Some(value) = value exists
  • None = value doesn't exist
  • Type system forces checking
  • No silent null checks possible

Pattern matching:

  • Option.match()
  • Handle both cases explicitly
  • Compiler warns if you miss one

Chaining:

  • option.map().flatMap().match()
  • Pipeline of operations
  • Null-safe by design


🟠 Advanced Patterns

Optional Pattern 2: Optional Chaining and Composition

Rule: Use Option combinators (map, flatMap, ap) to compose operations that may fail, creating readable and maintainable pipelines.

Good Example:

This example demonstrates optional chaining patterns.

import { Effect, Option, pipe } from "effect";

interface User {
  id: string;
  name: string;
  email: string;
}

interface Profile {
  bio: string;
  website?: string;
  avatar?: string;
}

interface Settings {
  theme: "light" | "dark";
  notifications: boolean;
  language: string;
}

const program = Effect.gen(function* () {
  console.log(`\n[OPTIONAL CHAINING] Composing Option operations\n`);

  // Example 1: Simple chain with map
  console.log(`[1] Chaining transformations with map():\n`);

  const userId: Option.Option<string> = Option.some("user-42");

  const userDisplayId = Option.map(userId, (id) => `User#${id}`);

  const idMessage = Option.match(userDisplayId, {
    onSome: (display) => display,
    onNone: () => "No user ID",
  });

  yield* Effect.log(`[CHAIN 1] ${idMessage}`);

  // Chained maps
  const email: Option.Option<string> = Option.some("[email protected]");

  const emailParts = pipe(
    email,
    Option.map((e) => e.toLowerCase()),
    Option.map((e) => e.split("@")),
    Option.map((parts) => parts[0]) // username
  );

  const username = Option.getOrElse(emailParts, () => "unknown");

  yield* Effect.log(`[USERNAME] ${username}\n`);

  // Example 2: FlatMap for chaining operations that return Option
  console.log(`[2] Chaining operations with flatMap():\n`);

  const findUser = (id: string): Option.Option<User> =>
    id === "user-42"
    

---

*Content truncated.*

When not to use it

  • When working with non-Effect-TS applications
  • When simple null checks are sufficient for the codebase

Prerequisites

effect package

Limitations

  • Requires adoption of Effect-TS patterns
  • Adds complexity to simple data structures

How it compares

Unlike standard null checks which are prone to runtime errors, this approach uses a type system to ensure all cases are handled at compile time.

Compared to similar skills

effect-patterns-value-handling side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
effect-patterns-value-handling (this skill)17moNo flagsIntermediate
typescript-review392moNo flagsIntermediate
typescript282moNo flagsBeginner
react-patterns96moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry