interface / @typed/fx/Sink

Sink.WithStateSemaphore

An early-exit sink with serialized effectful access to consumer-local state.

Package version
2.0.0-beta.13
Category
Stateful delivery
Since
1.0.0
Member of
Sink

Import

import { Sink } from "@typed/fx/Sink";

Access Sink.WithStateSemaphore through the imported Sink export. Its declaration below describes the member.

Signatures

interface WithStateSemaphore<A, E, R, B> extends WithEarlyExit<A, E, R> {
    readonly modifyEffect: <C, E2, R2>(f: (state: B) => Effect<readonly [
        C,
        B
    ], E2, R2>) => Effect<C, E | E2, R | R2>;
    readonly updateEffect: <E2, R2>(f: (state: B) => Effect<B, E2, R2>) => Effect<B, E | E2, R | R2>;
    readonly get: Effect<B, E, R>;
}

Why

Effectful reducers may overlap; semaphore-guarded operations prevent lost updates while keeping their typed errors and service requirements visible.

Ownership and lifetime

withStateSemaphore owns the mutable cell and semaphore for one invocation. Each modify/update holds one permit until its effect completes or is interrupted, then releases it automatically.

Property: get

Reads the current state under the same semaphore as updates.

Property: get: Why

Locked reads cannot observe an in-progress effectful transition.

Property: get: Ownership and lifetime

The read holds one permit briefly and acquires no resource beyond the invocation-owned semaphore.

Property: modifyEffect

Atomically derives a result and replacement state under the sink’s semaphore.

Property: modifyEffect: Why

Returning a separate result avoids a second unlocked read after an update.

Property: modifyEffect: Ownership and lifetime

One semaphore permit is held for the entire Effect and released on completion or interruption. The state changes only when the Effect succeeds.

Property: updateEffect

Atomically replaces state and returns the replacement value.

Property: updateEffect: Why

It is the direct serialized form of an effectful state transition.

Property: updateEffect: Ownership and lifetime

One semaphore permit is held until the transition completes or is interrupted. Failure leaves the state unchanged.

Examples

import { Effect } from "effect"
import type * as Sink from "@typed/fx/Sink"
const increment = (sink: Sink.Sink.WithStateSemaphore<number, never, never, number>) =>
  sink.updateEffect((n) => Effect.succeed(n + 1))

Other public imports

These import paths expose the same declaration. Each page retains its own public name and signature.

Source