# SubscriptionRef

A `SubscriptionRef<A>` is a [`Ref`](https://effect.plants.sh/state-management/ref/) you can *observe*. It
holds the current value like any reference, but it also exposes a
[`Stream`](https://effect.plants.sh/streaming/) of changes: subscribe to it and you receive the current
value immediately, followed by every subsequent update.

This is the bridge between mutable state and reactive consumers. Use it for state
that drives a UI, a live dashboard, a health indicator — anything that needs to
*react* to changes rather than poll for them.

## Observing changes

The defining operation is `changes`, which returns a `Stream` that replays the
current value and then emits each future update.

```ts
import { Deferred, Effect, Fiber, Stream, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(0)
  // Used to make sure the subscriber is attached before we start updating.
  const ready = yield* Deferred.make<void>()

  // Fork a fiber that collects the first three values it observes.
  const fiber = yield* SubscriptionRef.changes(ref).pipe(
    // Signal "ready" as soon as the first (current) value arrives.
    Stream.tap(() => Deferred.succeed(ready, void 0)),
    Stream.take(3),
    Stream.runCollect,
    Effect.forkChild
  )

  // Wait until the subscriber has received the initial value...
  yield* Deferred.await(ready)
  // ...then push two updates; both are published to the subscriber.
  yield* SubscriptionRef.set(ref, 1)
  yield* SubscriptionRef.set(ref, 2)

  return yield* Fiber.join(fiber) // [0, 1, 2]
})
```

The subscriber sees `0` (the value at the moment it subscribed), then `1` and `2`.
A subscriber that attaches *later* would still receive the latest value first,
because `SubscriptionRef` replays the most recent value to every new subscriber.
**Reading vs. observing:** `SubscriptionRef.get` reads the current value once, just like `Ref.get`. Use
  `changes` only when you want to *react* to every update over time. The same
  setters — `set`, `update`, `modify`, and their effectful and `Some` variants —
  publish to subscribers automatically.

## Updating the value

`SubscriptionRef` carries the full `Ref`-style update API, and every successful
update is published to current subscribers. Because updates run under an internal
semaphore, the effectful variants (`updateEffect`, `modifyEffect`, ...) are
serialized the same way [`SynchronizedRef`](https://effect.plants.sh/state-management/synchronized-ref/)
serializes them.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)

  // Pure update — publishes the new value (20) to subscribers.
  yield* SubscriptionRef.update(ref, (n) => n * 2)

  // Effectful update — serialized, and also published once it commits.
  yield* SubscriptionRef.updateEffect(ref, (n) =>
    Effect.succeed(n + 5))

  return yield* SubscriptionRef.get(ref) // 25
})
```

## Reactive shared state through a service

The natural home for a `SubscriptionRef` is a [service](https://effect.plants.sh/services-and-layers/)
that owns a piece of shared state and lets the rest of the app both mutate it and
subscribe to it. Here a service tracks application status; a background worker
flips the status while a logger reacts to every transition.

```ts
import { Context, Effect, Fiber, Layer, Stream, SubscriptionRef } from "effect"

type Status = "starting" | "ready" | "degraded"

class Health extends Context.Service<Health, {
  readonly set: (status: Status) => Effect.Effect<void>
  readonly changes: Stream.Stream<Status>
}>()("app/Health") {
  static layer = Layer.effect(
    Health,
    Effect.gen(function*() {
      const ref = yield* SubscriptionRef.make<Status>("starting")
      return Health.of({
        set: (status) => SubscriptionRef.set(ref, status),
        // Expose the change stream so any consumer can react to transitions.
        changes: SubscriptionRef.changes(ref)
      })
    })
  )
}

const program = Effect.gen(function*() {
  const health = yield* Health

  // A consumer that logs every status it observes, including the initial one.
  const logger = yield* health.changes.pipe(
    Stream.take(3),
    Stream.runForEach((status) => Effect.log(`status -> ${status}`)),
    Effect.forkChild
  )

  // Drive some transitions; each is published to the logger above.
  yield* health.set("ready")
  yield* health.set("degraded")

  yield* Fiber.join(logger)
}).pipe(Effect.provide(Health.layer))
```

The logger prints `starting`, then `ready`, then `degraded` — it reacts to state
changes without ever polling, and any number of consumers can subscribe
independently.

## When to use it

Choose `SubscriptionRef` when something needs to *observe* state as it evolves. If
you only ever read the current value, a plain [`Ref`](https://effect.plants.sh/state-management/ref/) is
simpler and lighter. If you need serialized effectful updates but no subscribers,
use [`SynchronizedRef`](https://effect.plants.sh/state-management/synchronized-ref/). For everything you
can build on top of the change stream, see [Streaming](https://effect.plants.sh/streaming/).

## Reference

A `SubscriptionRef<A>` has the **same operation surface as
[`SynchronizedRef`](https://effect.plants.sh/state-management/synchronized-ref/)** — the full set of pure
operations plus their `*Effect` variants, all serialized under an internal
semaphore so concurrent updates never interleave — **plus** the observable
[`changes`](#changes) stream. Every successful `set`, every non-empty `update`,
and every committed `modify` publishes the new value to current subscribers.
**Backpressure and the replay buffer:** Internally each `SubscriptionRef` is backed by an unbounded
  [`PubSub`](https://effect.plants.sh/concurrency/queue-and-pubsub/) with a replay buffer of size `1`.
  That is why [`changes`](#changes) always starts every new subscriber with the
  *current* value before emitting future updates. Because the underlying PubSub is
  unbounded, publishing never blocks the writer; slow subscribers accumulate
  pending values in their own queue rather than backpressuring the producer. For
  the streaming side of this, see [Streaming](https://effect.plants.sh/streaming/).

### isSubscriptionRef

Type guard that returns `true` when the value is a `SubscriptionRef`. Useful for
narrowing an `unknown` before calling subscription-ref operations.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(0)

  SubscriptionRef.isSubscriptionRef(ref) // => true
  SubscriptionRef.isSubscriptionRef(42) // => false
})
```

### make

Constructs a `SubscriptionRef` from an initial value. The initial value is
published during construction, so [`changes`](#changes) starts new subscribers
with it.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(0)
  return yield* SubscriptionRef.get(ref) // => 0
})
```

### changes

Returns a [`Stream`](https://effect.plants.sh/streaming/) that first emits the current value and then
emits every subsequent published value. Each new subscriber independently
receives the latest value first thanks to the replay buffer.

```ts
import { Effect, Fiber, Stream, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(0)

  // Subscribe (replays the current value 0), then drive an update.
  const fiber = yield* SubscriptionRef.changes(ref).pipe(
    Stream.take(2),
    Stream.runCollect,
    Effect.forkChild
  )
  yield* SubscriptionRef.set(ref, 1)

  return yield* Fiber.join(fiber) // => [0, 1]
})
```

### get

Reads the current value once, without subscribing. Equivalent to `Ref.get`.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(42)
  return yield* SubscriptionRef.get(ref) // => 42
})
```

### getUnsafe

Reads the current value synchronously, bypassing the semaphore. Only safe when
the caller is certain there are no concurrent modifications.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(42)
  return SubscriptionRef.getUnsafe(ref) // => 42 (plain value, not an Effect)
})
```

### set

Replaces the value and publishes it to subscribers.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(0)
  yield* SubscriptionRef.set(ref, 42)
  return yield* SubscriptionRef.get(ref) // => 42
})
```

### getAndSet

Sets a new value and returns the **previous** value, publishing the change.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.getAndSet(ref, 20) // => 10 (old value)
})
```

### setAndGet

Sets a new value and returns that **new** value, publishing the change.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(0)
  return yield* SubscriptionRef.setAndGet(ref, 42) // => 42
})
```

### update

Applies a function to the current value and publishes the result.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  yield* SubscriptionRef.update(ref, (n) => n * 2)
  return yield* SubscriptionRef.get(ref) // => 20
})
```

### updateAndGet

Applies a function and returns the **new** value, publishing the change.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.updateAndGet(ref, (n) => n * 2) // => 20
})
```

### getAndUpdate

Applies a function and returns the **previous** value, publishing the change.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.getAndUpdate(ref, (n) => n * 2) // => 10 (old value)
})
```

### updateSome

Applies a partial update. On `Option.some` the value is set and published; on
`Option.none` the reference is left unchanged and nothing is published.

```ts
import { Effect, Option, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  yield* SubscriptionRef.updateSome(
    ref,
    (n) => n > 5 ? Option.some(n * 2) : Option.none()
  )
  return yield* SubscriptionRef.get(ref) // => 20
})
```

### updateSomeAndGet

Like [`updateSome`](#updatesome), but returns the value after the update
decision — the new value on `some`, or the unchanged current value on `none`.

```ts
import { Effect, Option, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.updateSomeAndGet(
    ref,
    (n) => n > 5 ? Option.some(n * 2) : Option.none()
  ) // => 20
})
```

### getAndUpdateSome

Like [`updateSome`](#updatesome), but returns the **previous** value regardless
of whether the update was applied.

```ts
import { Effect, Option, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.getAndUpdateSome(
    ref,
    (n) => n > 5 ? Option.some(n * 2) : Option.none()
  ) // => 10 (old value)
})
```

### modify

Computes a `[result, newValue]` pair: the new value is set and published, and the
separate `result` is returned. Use it to read and write in one atomic step.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.modify(ref, (n) => [`was ${n}`, n * 2])
  // => "was 10" (ref now holds 20)
})
```

### modifySome

Like [`modify`](#modify), but the second element of the pair is an `Option`. On
`Option.some` the value is updated and published; on `Option.none` only the
`result` is returned and nothing is published.

```ts
import { Effect, Option, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.modifySome(
    ref,
    (n) => n > 5 ? ["updated", Option.some(n * 2)] : ["unchanged", Option.none()]
  ) // => "updated" (ref now holds 20)
})
```

### updateEffect

Effectful variant of [`update`](#update). The effect runs under the semaphore;
the produced value is set and published once it commits.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  yield* SubscriptionRef.updateEffect(ref, (n) => Effect.succeed(n + 5))
  return yield* SubscriptionRef.get(ref) // => 15
})
```

### updateAndGetEffect

Effectful variant of [`updateAndGet`](#updateandget) — returns the new value.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.updateAndGetEffect(
    ref,
    (n) => Effect.succeed(n + 5)
  ) // => 15
})
```

### getAndUpdateEffect

Effectful variant of [`getAndUpdate`](#getandupdate) — returns the previous value.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.getAndUpdateEffect(
    ref,
    (n) => Effect.succeed(n + 5)
  ) // => 10 (old value)
})
```

### updateSomeEffect

Effectful variant of [`updateSome`](#updatesome). On effectful `Option.some` the
value is set and published; on `Option.none` nothing changes.

```ts
import { Effect, Option, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  yield* SubscriptionRef.updateSomeEffect(
    ref,
    (n) => Effect.succeed(n > 5 ? Option.some(n + 3) : Option.none())
  )
  return yield* SubscriptionRef.get(ref) // => 13
})
```

### updateSomeAndGetEffect

Effectful variant of [`updateSomeAndGet`](#updatesomeandget) — returns the value
after the update decision.

```ts
import { Effect, Option, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.updateSomeAndGetEffect(
    ref,
    (n) => Effect.succeed(n > 5 ? Option.some(n + 3) : Option.none())
  ) // => 13
})
```

### getAndUpdateSomeEffect

Effectful variant of [`getAndUpdateSome`](#getandupdatesome) — returns the
previous value.

```ts
import { Effect, Option, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.getAndUpdateSomeEffect(
    ref,
    (n) => Effect.succeed(n > 5 ? Option.some(n + 3) : Option.none())
  ) // => 10 (old value)
})
```

### modifyEffect

Effectful variant of [`modify`](#modify). The effect yields a `[result, newValue]`
pair; the new value is set and published, the result returned.

```ts
import { Effect, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.modifyEffect(
    ref,
    (n) => Effect.succeed([`doubled ${n}`, n * 2] as const)
  ) // => "doubled 10" (ref now holds 20)
})
```

### modifySomeEffect

Effectful variant of [`modifySome`](#modifysome). The effect yields a
`[result, Option<newValue>]` pair; the value is published only on `Option.some`.

```ts
import { Effect, Option, SubscriptionRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* SubscriptionRef.make(10)
  return yield* SubscriptionRef.modifySomeEffect(
    ref,
    (n) =>
      Effect.succeed(
        n > 5
          ? (["updated", Option.some(n + 5)] as const)
          : (["unchanged", Option.none()] as const)
      )
  ) // => "updated" (ref now holds 15)
})
```