Token导航 LogoToken导航TokenDH.com
前端设计需要联网github未标认证来源可访问许可证需确认审计通过

effect-core-patterns效果核心模式

Agent Skill

effect-core-patterns 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

665

周安装

28

GitHub Stars

142

下载量

233
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:effect-core-patterns(效果核心模式)
来源仓库:https://github.com/thebushidocollective/han
仓库路径:skills/effect-core-patterns
安装命令:
npx skills add https://github.com/thebushidocollective/han --skill effect-core-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/thebushidocollective/han --skill effect-core-patterns

简介

effect-core-patterns 讲解 Effect 核心编程模式,包括 Effect.gen 生成器与管道组合。

  • 适合初学者掌握 Effect 的类型安全与声明式风格。
  • 通过 GitHub 安装,使用 npx skills add 命令添加指定仓库的 skill/effect-core-patterns 路径。
  • 重点在于理解 Effect 三型参含义与作用域管理。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Effect Core Patterns

Master the core Effect patterns for building type-safe, composable applications with Effect. This skill covers the Effect type, constructors, and composition patterns using Effect.gen.

The Effect Type

The Effect type has three type parameters:

Effect<Success, Error, Requirements>
  • Success (A): The type of value that an effect can succeed with
  • Error (E): The expected errors that can occur (use never for no errors)
  • Requirements (R): The contextual dependencies required (use never for no dependencies)
import { Effect } from "effect"

// Effect that succeeds with number, never fails, no requirements
const simpleEffect: Effect.Effect<number, never, never> = Effect.succeed(42)

// Effect that can fail with string error
const failableEffect: Effect.Effect<number, string, never> =
  Effect.fail("Something went wrong")

// Effect that requires a UserService
interface UserService {
  getUser: (id: string) => Effect.Effect<User, DbError, never>
}

const effectWithDeps: Effect.Effect<User, DbError, UserService> =
  Effect.gen(function* () {
    const userService = yield* Effect.service(UserService)
    const user = yield* userService.getUser("123")
    return user
  })

Creating Effects

Effect.succeed - Always Succeeds

Use when you have a pure value and need an Effect:

import { Effect } from "effect"

const result = Effect.succeed(42)
// Effect<number, never, never>

const user = Effect.succeed({ id: "1", name: "Alice" })
// Effect<User, never, never>

// Void effect (produces no useful value)
const voidEffect = Effect.succeed(undefined)
// Effect<void, never, never>

Effect.fail - Expected Failure

Use for recoverable, expected errors:

import { Effect } from "effect"

interface ValidationError {
  _tag: "ValidationError"
  message: string
}

const validateAge = (age: number): Effect.Effect<number, ValidationError, never> => {
  if (age < 0) {
    return Effect.fail({
      _tag: "ValidationError",
      message: "Age must be positive"
    })
  }
  return Effect.succeed(age)
}

// Usage with Effect.gen
const program = Effect.gen(function* () {
  const age = yield* validateAge(-5) // This will fail
  return age
})

Effect.sync - Synchronous Side Effects

Use for synchronous operations with side effects:

import { Effect } from "effect"

// Reading from a mutable variable
let counter = 0

const incrementCounter = Effect.sync(() => {
  counter++
  return counter
})

// Logging
const log = (message: string) =>
  Effect.sync(() => {
    console.log(message)
  })

// Current timestamp
const now = Effect.sync(() => Date.now())

// IMPORTANT: The function should not throw
// Thrown errors become "defects" (unexpected failures)

Effect.try - Synchronous Operations That May Fail

Use for sync operations that might throw:

import { Effect } from "effect"

// Parse JSON safely
const parseJSON = (text: string): Effect.Effect<unknown, Error, never> =>
  Effect.try(() => JSON.parse(text))

// With custom error mapping
interface ParseError {
  _tag: "ParseError"
  message: string
}

const parseJSONCustom = (text: string): Effect.Effect<unknown, ParseError, never> =>
  Effect.try({
    try: () => JSON.parse(text),
    catch: (error) => ({
      _tag: "ParseError",
      message: error instanceof Error ? error.message : String(error)
    })
  })

// Usage
const program = Effect.gen(function* () {
  const data = yield* parseJSON('{"name": "Alice"}')
  return data
})

Effect.promise - Async Operations (No Errors)

Use for promises that should never reject:

import { Effect } from "effect"

// Delayed execution
const delay = (ms: number): Effect.Effect<void, never, never> =>
  Effect.promise(() =>
    new Promise<void>((resolve) => setTimeout(resolve, ms))
  )

// Fetch with assumption it won't fail
const fetchData = (url: string): Effect.Effect<Response, never, never> =>
  Effect.promise(() => fetch(url))

// IMPORTANT: If promise rejects, it becomes a "defect"
// Use Effect.tryPromise for operations that can fail

Effect.tryPromise - Async Operations That May Fail

Use for promises that might reject:

import { Effect } from "effect"

interface NetworkError {
  _tag: "NetworkError"
  message: string
  statusCode?: number
}

const fetchUser = (id: string): Effect.Effect<User, NetworkError, never> =>
  Effect.tryPromise({
    try: async () => {
      const response = await fetch(`/api/users/${id}`)
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`)
      }
      return response.json()
    },
    catch: (error) => ({
      _tag: "NetworkError",
      message: error instanceof Error ? error.message : String(error),
      statusCode: error instanceof Error && 'status' in error
        ? (error as any).status
        : undefined
    })
  })

// Simplified version (errors become UnknownException)
const fetchUserSimple = (id: string): Effect.Effect<User, UnknownException, never> =>
  Effect.tryPromise(() => fetch(`/api/users/${id}`).then(r => r.json()))

Effect.async - Callback-Based APIs

Use for wrapping callback-style APIs:

import { Effect } from "effect"

// Wrap setTimeout
const sleep = (ms: number): Effect.Effect<void, never, never> =>
  Effect.async<void>((resume) => {
    const timeoutId = setTimeout(() => {
      resume(Effect.succeed(undefined))
    }, ms)

    // Optional cleanup on interruption
    return Effect.sync(() => {
      clearTimeout(timeoutId)
    })
  })

// Wrap Node.js callback API
interface FileError {
  _tag: "FileError"
  message: string
}

const readFile = (path: string): Effect.Effect<string, FileError, never> =>
  Effect.async<string, FileError>((resume) => {
    fs.readFile(path, 'utf8', (error, data) => {
      if (error) {
        resume(Effect.fail({
          _tag: "FileError",
          message: error.message
        }))
      } else {
        resume(Effect.succeed(data))
      }
    })
  })

Composing Effects with Effect.gen

Effect.gen allows you to write effect code using generator syntax:

import { Effect } from "effect"

// Basic composition
const program = Effect.gen(function* () {
  const a = yield* Effect.succeed(10)
  const b = yield* Effect.succeed(20)
  return a + b
})

// With error handling
const programWithErrors = Effect.gen(function* () {
  const age = yield* validateAge(25)
  const user = yield* createUser({ age })
  return user
})

// Sequential operations
const fetchUserProfile = (userId: string) =>
  Effect.gen(function* () {
    const user = yield* fetchUser(userId)
    const posts = yield* fetchPosts(user.id)
    const comments = yield* fetchComments(user.id)
    return { user, posts, comments }
  })

// Using control flow
const processData = (data: unknown) =>
  Effect.gen(function* () {
    const validated = yield* validateData(data)

    if (validated.type === "user") {
      const user = yield* createUser(validated)
      return { type: "user", user }
    } else {
      const post = yield* createPost(validated)
      return { type: "post", post }
    }
  })

// Error handling with short-circuiting
const safeDivide = (a: number, b: number) =>
  Effect.gen(function* () {
    if (b === 0) {
      yield* Effect.fail({ _tag: "DivideByZero" })
      return // Explicit return for type narrowing
    }
    return a / b
  })

Running Effects

Effect.runSync - Synchronous Execution

Use for effects with no async operations or requirements:

import { Effect } from "effect"

const result = Effect.runSync(Effect.succeed(42))
// 42

// Throws if effect can fail
try {
  Effect.runSync(Effect.fail("error"))
} catch (error) {
  // Caught
}

// CANNOT use with async effects or requirements
// Effect.runSync(Effect.promise(() => fetch("..."))) // Runtime error!

Effect.runPromise - Async Execution

Use for async effects without requirements:

import { Effect } from "effect"

const program = Effect.gen(function* () {
  yield* delay(1000)
  return "Done"
})

const result = await Effect.runPromise(program)
// "Done" after 1 second

// Rejects on failure
try {
  await Effect.runPromise(Effect.fail("error"))
} catch (error) {
  // error === "error"
}

Effect.runPromiseExit - Get Full Exit Information

Use when you need detailed success/failure information:

import { Effect, Exit } from "effect"

const program = Effect.succeed(42)

const exit = await Effect.runPromiseExit(program)

if (Exit.isSuccess(exit)) {
  console.log("Success:", exit.value)
} else if (Exit.isFailure(exit)) {
  console.log("Failure:", exit.cause)
}

Building Pipelines

Effect.map - Transform Success Values

import { Effect, pipe } from "effect"

const double = (n: number) => n * 2

// Using pipe
const result = pipe(
  Effect.succeed(21),
  Effect.map(double)
)
// Effect<42, never, never>

// Using method
const result2 = Effect.succeed(21).pipe(
  Effect.map(double)
)

// Chaining transformations
const program = pipe(
  Effect.succeed("hello"),
  Effect.map(s => s.toUpperCase()),
  Effect.map(s => s.length)
)
// Effect<5, never, never>

Effect.flatMap - Chain Dependent Effects

import { Effect, pipe } from "effect"

const getUser = (id: string): Effect.Effect<User, DbError, never> => {
  // ...
}

const getUserPosts = (userId: string): Effect.Effect<Post[], DbError, never> => {
  // ...
}

// Using pipe
const program = pipe(
  getUser("123"),
  Effect.flatMap(user => getUserPosts(user.id))
)

// Using Effect.gen (more readable)
const program2 = Effect.gen(function* () {
  const user = yield* getUser("123")
  const posts = yield* getUserPosts(user.id)
  return posts
})

Effect.andThen - Sequential Composition

import { Effect, pipe } from "effect"

// Chain effects, ignoring previous result
const program = pipe(
  log("Starting..."),
  Effect.andThen(processData()),
  Effect.andThen(log("Done!"))
)

// Provide value to next effect
const program2 = pipe(
  Effect.succeed(5),
  Effect.andThen(n => Effect.succeed(n * 2))
)

Effect.tap - Side Effects Without Changing Value

import { Effect, pipe } from "effect"

const program = pipe(
  fetchUser("123"),
  Effect.tap(user => log(`Fetched user: ${user.name}`)),
  Effect.tap(user => saveToCache(user)),
  Effect.map(user => user.email)
)

// The taps run but don't change the flowing value

Effect Transformations

Effect.mapError - Transform Errors

import { Effect, pipe } from "effect"

interface DbError {
  _tag: "DbError"
  message: string
}

interface AppError {
  _tag: "AppError"
  message: string
  context: string
}

const program = pipe(
  queryDatabase(),
  Effect.mapError((dbError: DbError): AppError => ({
    _tag: "AppError",
    message: dbError.message,
    context: "user-service"
  }))
)

Effect.mapBoth - Transform Success and Error

import { Effect, pipe } from "effect"

const program = pipe(
  Effect.succeed(10),
  Effect.mapBoth({
    onSuccess: (n) => n * 2,
    onFailure: (e) => ({ _tag: "MappedError", original: e })
  })
)

Effect.orElse - Fallback on Failure

import { Effect, pipe } from "effect"

const program = pipe(
  fetchFromPrimaryDb(),
  Effect.orElse(() => fetchFromSecondaryDb())
)

// Fallback to different effect based on error
const programWithCheck = pipe(
  riskyOperation(),
  Effect.orElse((error) =>
    error._tag === "Timeout"
      ? retryOperation()
      : Effect.fail(error)
  )
)

Best Practices

  1. Use Effect.gen for Readability: Prefer Effect.gen over pipe for complex compositions with multiple steps.
  2. Type Your Errors: Always use tagged unions for error types to enable catchTag and better error handling.
  3. Distinguish Errors from Defects: Use Effect.try/tryPromise for operations that can fail. Let unexpected errors become defects.
  4. Keep Effects Pure: Don't perform side effects outside of Effect constructors. Use Effect.sync for side effects.
  5. Use Descriptive Names: Name effects based on what they do, not how they do it (e.g., fetchUser not makeHttpRequest).
  6. Compose Small Effects: Build complex operations from small, focused effects that do one thing well.
  7. Handle Requirements Explicitly: Use Effect.service and layers to manage dependencies rather than importing directly.
  8. Document Effect Types: Explicitly type effects to make requirements, errors, and success types clear.
  9. Use pipe for Transformations: For simple transformations, pipe is more concise than Effect.gen.
  10. Test Effects Independently: Design effects to be testable by injecting dependencies via requirements.

Common Pitfalls

  1. Using runSync on Async Effects: runSync throws on async effects. Use runPromise instead.
  2. Not Handling Errors: Forgetting that effects can fail. Always consider the error channel.
  3. Mixing Promises and Effects: Converting between promises and effects incorrectly. Use Effect.promise/tryPromise.
  4. Ignoring Requirements: Not providing required services causes runtime errors. Use layers properly.
  5. Throwing in Effect.sync: Thrown errors become defects. Use Effect.try for operations that can throw.
  6. Not Using Effect.gen: Complex pipe chains are hard to read. Use Effect.gen for better readability.
  7. Incorrect Error Types: Using unknown or Error instead of specific tagged error types.
  8. Sequential When Parallel Is Better: Using Effect.gen sequentially when operations could run in parallel with Effect.all.
  9. Over-Using map/flatMap: Effect.gen is clearer for multi-step operations than nested maps.
  10. Not Leveraging Type Safety: Not using TypeScript's type system to catch errors at compile time.

When to Use This Skill

Use effect-core-patterns when you need to:

  • Build type-safe applications with Effect
  • Create and compose effectful operations
  • Handle errors in a type-safe manner
  • Work with async operations and promises
  • Manage side effects explicitly
  • Create pipelines of transformations
  • Convert callback-based APIs to Effect
  • Build maintainable, composable code
  • Leverage functional programming patterns
  • Ensure compile-time safety for effects

Resources

Official Documentation

Guides

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

32.62%
按下载量换算76

Claude

30.48%
按下载量换算71

Cursor

20.09%
按下载量换算47

Gemini CLI

9.39%
按下载量换算22

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills