SuperAPI AI 工具集

state management

编程与开发

查看项目

更新于 10-16 10:44

技能介绍

用途

该技能用于指导在 Effect(TypeScript 函数式效果库)中管理可变状态。当用户询问 Effect Ref、可变状态、Ref.make、Ref.get、Ref.set、Ref.update、SubscriptionRef 等相关主题时启用。

主要能力

Effect 提供三种函数式可变状态原语,均为 fiber 安全,可在并发访问下正确工作:

  • Ref:基础可变引用。
  • SynchronizedRef:支持带效果(effectful)更新的 Ref。
  • SubscriptionRef:支持变更通知的 Ref。

使用方式

Ref 基础用法

创建与操作 Ref:

import { Effect, Ref } from "effect"

const program = Effect.gen(function* () {
  const counter = yield* Ref.make(0)
  const current = yield* Ref.get(counter)
  yield* Ref.set(counter, 10)
  yield* Ref.update(counter, (n) => n + 1)
  const old = yield* Ref.getAndSet(counter, 0)
  const newValue = yield* Ref.updateAndGet(counter, (n) => n + 5)
  const [oldVal, result] = yield* Ref.modify(counter, (n) => [n, n * 2])
})

原子操作

const atomicIncrement = Effect.gen(function* () {
  const counter = yield* Ref.make(0)
  yield* Effect.all([
    Ref.update(counter, (n) => n + 1),
    Ref.update(counter, (n) => n + 1),
    Ref.update(counter, (n) => n + 1)
  ], { concurrency: "unbounded" })
  return yield* Ref.get(counter)
})

在 Service 中使用 Ref

const CounterService = Effect.gen(function* () {
  const ref = yield* Ref.make(0)
  return {
    increment: Ref.update(ref, (n) => n + 1),
    decrement: Ref.update(ref, (n) => n - 1),
    get: Ref.get(ref),
    reset: Ref.set(ref, 0)
  }
})
const CounterLive = Layer.effect(Counter, CounterService)

SynchronizedRef:带效果的更新

适用于更新过程需要运行效果的情况:

import { Effect, SynchronizedRef } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* SynchronizedRef.make({ count: 0, lastUpdated: Date.now() })
  yield* SynchronizedRef.updateEffect(ref, (state) =>
    Effect.gen(function* () {
      yield* Effect.log("Updating state")
      return { count: state.count + 1, lastUpdated: Date.now() }
    })
  )
  const result = yield* SynchronizedRef.modifyEffect(ref, (state) =>
    Effect.gen(function* () {
      const newCount = state.count + 1
      yield* sendMetric("counter", newCount)
      return [newCount, { ...state, count: newCount }]
    })
  )
})

何时使用 SynchronizedRef:更新需要 API 调用、需要日志/指标、依赖外部状态或需要错误处理时。例如带异步刷新的缓存:

const cache = yield* SynchronizedRef.make<Data | null>(null)
const refreshCache = SynchronizedRef.updateEffect(cache, () =>
  Effect.tryPromise(() => fetchLatestData())
)

SubscriptionRef:响应式状态

适用于需要通知订阅者的状态:

import { Effect, SubscriptionRef, Stream } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* SubscriptionRef.make(0)
  const changes = yield* SubscriptionRef.changes(ref)
  yield* Effect.fork(
    Stream.runForEach(changes, (value) => Effect.log(`Value changed to: ${value}`))
  )
  yield* SubscriptionRef.set(ref, 1)
  yield* SubscriptionRef.update(ref, (n) => n + 1)
  yield* SubscriptionRef.set(ref, 10)
})

响应式模式示例,多个订阅者监听配置变更:

const configRef = yield* SubscriptionRef.make(initialConfig)
const subscriber1 = Effect.fork(
  Stream.runForEach(SubscriptionRef.changes(configRef), (config) => updateService1(config))
)
const subscriber2 = Effect.fork(
  Stream.runForEach(SubscriptionRef.changes(configRef), (config) => updateService2(config))
)
yield* SubscriptionRef.set(configRef, newConfig)

三者对比

特性 Ref SynchronizedRef SubscriptionRef
基础 get/set ✅ ✅ ✅
原子更新 ✅ ✅ ✅
带效果更新 ❌ ✅ ❌
变更通知 ❌ ❌ ✅
适用场景 简单状态 异步更新 响应式状态

常见模式

计数器 Service

class Counter extends Context.Tag("Counter")<
  Counter,
  {
    readonly increment: Effect.Effect<number>
    readonly decrement: Effect.Effect<number>
    readonly get: Effect.Effect<number>
  }
>() {}

const CounterLive = Layer.effect(
  Counter,
  Effect.gen(function* () {
    const ref = yield* Ref.make(0)
    return {
      increment: Ref.updateAndGet(ref, (n) => n + 1),
      decrement: Ref.updateAndGet(ref, (n) => n - 1),
      get: Ref.get(ref)
    }
  })
)

状态机

type State = "idle" | "loading" | "success" | "error"

const stateMachine = Effect.gen(function* () {
  const state = yield* Ref.make<State>("idle")
  const transition = (from: State, to: State) =>
    Ref.modify(state, (current) => current === from ? [true, to] : [false, current])
  return {
    state: Ref.get(state),
    startLoading: transition("idle", "loading"),
    succeed: transition("loading", "success"),
    fail: transition("loading", "error"),
    reset: Ref.set(state, "idle")
  }
})

累加器

const accumulator = Effect.gen(function* () {
  const items = yield* Ref.make<Array<Item>>([])
  return {
    add: (item: Item) => Ref.update(items, (arr) => [...arr, item]),
    getAll: Ref.get(items),
    clear: Ref.set(items, []),
    count: Effect.map(Ref.get(items), (arr) => arr.length)
  }
})

最佳实践

  • 使用 Ref 处理简单状态:基础计数器、标志、累加器。
  • 使用 SynchronizedRef 处理异步更新:当更新需要效果时。
  • 使用 SubscriptionRef 处理响应式模式:当其他部分需要通知时。
  • 保持状态最小化:不要存储派生数据。
  • 优先不可变更新:返回新对象,不要直接变更。

适用场景

需要使用 Effect 管理并发安全状态、异步缓存刷新、响应式配置更新,或在 Service 中封装计数器、状态机、累加器等逻辑的 TypeScript 项目。

附加资源

关于状态管理的完整文档,可查阅 ${CLAUDE_PLUGIN_ROOT}/references/llms-full.txt,搜索 "Ref"(基础可变引用)、"SynchronizedRef"(带效果更新)、"SubscriptionRef"(响应式状态)等章节。

限制

资料未说明该技能的运行环境依赖、版本要求或收费信息。