state management
8
总安装量
0
周安装量
#34945
全站排名
安装命令
npx skills add https://github.com/andrueandersoncs/claude-skill-effect-ts --skill State Management
Skill 文档
State Management in Effect
Overview
Effect provides functional mutable state primitives:
- Ref – Basic mutable reference
- SynchronizedRef – Ref with effectful updates
- SubscriptionRef – Ref with change notifications
All are fiber-safe and work correctly with concurrent access.
Ref – Basic Mutable Reference
Creating and Using Refs
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
])
})
Atomic Operations
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)
})
Ref in Services
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 – Effectful Updates
For updates that require running effects:
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 }
]
})
)
})
When to Use SynchronizedRef
- Updates require API calls
- Updates require logging/metrics
- Updates depend on external state
- Updates need error handling
// Cache with async refresh
const cache = yield* SynchronizedRef.make<Data | null>(null)
const refreshCache = SynchronizedRef.updateEffect(cache, () =>
Effect.tryPromise(() => fetchLatestData())
)
SubscriptionRef – Reactive State
For state that needs to notify subscribers:
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)
})
Reactive Patterns
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)
Comparison
| Feature | Ref | SynchronizedRef | SubscriptionRef |
|---|---|---|---|
| Basic get/set | â | â | â |
| Atomic updates | â | â | â |
| Effectful updates | â | â | â |
| Change notifications | â | â | â |
| Use case | Simple state | Async updates | Reactive state |
Common Patterns
Counter 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)
}
})
)
State Machine
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")
}
})
Accumulator
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)
}
})
Best Practices
- Use Ref for simple state – Basic counters, flags, accumulators
- Use SynchronizedRef for async updates – When updates need effects
- Use SubscriptionRef for reactive patterns – When others need notifications
- Keep state minimal – Don’t store derived data
- Prefer immutable updates – Return new objects, don’t mutate
Additional Resources
For comprehensive state management documentation, consult ${CLAUDE_PLUGIN_ROOT}/references/llms-full.txt.
Search for these sections:
- “Ref” for basic mutable references
- “SynchronizedRef” for effectful updates
- “SubscriptionRef” for reactive state