# Sink.WithStateSemaphore

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

## Signatures

```ts
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

```ts
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))
```
