interface / @typed/fx

RefSubject.RefSubject

A RefSubject is a mutable reference that can be observed as an Fx. It combines the capabilities of a Ref (get/set/update) with a Subject (subscribe).

Package version
2.0.0-beta.13
Category
Writable state
Since
1.0.0
Member of
RefSubject

Import

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

Access RefSubject.RefSubject through the imported RefSubject export. Its declaration below describes the member.

This public exposure is a re-export. Its import path is supported; declaration documentation is shared with the other public exposures below.

Signatures

export interface RefSubject<A, E = never, R = never> extends Computed<A, E, R>, Subject.Subject<A, E, R> {
    readonly [RefSubjectTypeId]: RefSubjectTypeId;
    readonly updates: <B, E2, R2>(f: (ref: GetSetDelete<A, E, R>) => Effect.Effect<B, E2, R2>) => Effect.Effect<B, E | E2, R | R2>;
    readonly interrupt: Effect.Effect<void, never, R>;
}
export declare namespace RefSubject {
    type Any = RefSubject<any, any, any> | RefSubject<any, any> | RefSubject<any, never, any> | RefSubject<any>;
    interface Service<Self, Id extends string, A, E> extends RefSubject<A, E, Self> {
        readonly id: Id;
        readonly service: Context.Service<Self, RefSubject<A, E>>;
        readonly make: <R = never>(value: A | Effect.Effect<A, E, R> | Fx<A, E, R>, options?: RefSubjectOptions<A> & {
            readonly skip?: number;
            readonly take?: number;
        }) => Layer.Layer<Self, never, Exclude<R, Scope.Scope>>;
        readonly layer: <E2, R2>(make: Effect.Effect<RefSubject<A, E>, E2, R2 | Scope.Scope>) => Layer.Layer<Self, E2, Exclude<R2, Scope.Scope>>;
    }
    interface Class<Self, Id extends string, A, E> extends RefSubject.Service<Self, Id, A, E> {
        new (): RefSubject.Service<Self, Id, A, E>;
    }
}

Why

Unifies synchronized mutable state, Effectful current reads, and Fx pushes so the same state can be tested and consumed without a renderer.

Ownership and lifetime

RefSubject is a contract and performs no acquisition. Implementations retain the errors, services, interruption, and Scope requirements expressed by its members.

Property: [[computed:[RefSubjectTypeId]]]

Carries the runtime marker recognized by isRefSubject.

Property: [[computed:[RefSubjectTypeId]]]: Why

Marks the value as a writable RefSubject for runtime guards while retaining its Computed and Subject capabilities.

Property: [[computed:[RefSubjectTypeId]]]: Ownership and lifetime

This declaration performs no acquisition and retains no resources. Implementations preserve source errors, services, and lifetime.

Property: interrupt

Interrupts the RefSubject, stopping all subscriptions and cleaning up resources.

Property: interrupt: Why

Interrupts the RefSubject, stopping all subscriptions and cleaning up resources.

Property: interrupt: Ownership and lifetime

Running this Effect returns void: it resets pending initialization, closes the RefSubject’s private Scope, interrupts its initializer fiber, and interrupts its Subject. It cannot fail; R supplies services captured by the RefSubject’s internal lifetime.

Property: updates

Runs an effect that can modify the RefSubject transactionally. All operations within the transaction are atomic and serialized.

Property: updates: Why

Runs an effect that can modify the RefSubject transactionally. All operations within the transaction are atomic and serialized. The transition is serialized at the RefSubject rather than coordinated by callers or UI components.

Property: updates: Ownership and lifetime

Running updates performs one serialized subject transition and returns the committed value. It acquires no resource and preserves source failures and services.

Examples

import { Effect } from "effect"
import { Fx, RefSubject } from "@typed/fx"

// Create a RefSubject with an initial value
const program = Effect.scoped(Effect.gen(function* () {
  const count = yield* RefSubject.make(0)

  // Get the current value
  const current = yield* count
  console.log(current) // 0

  // Update the value
  yield* RefSubject.set(count, 5)
  const updated = yield* count
  console.log(updated) // 5

  // Use as an Fx to observe changes
  yield* Effect.forkScoped(Fx.observe(
    count,
    (value) => Effect.sync(() => console.log("Count changed:", value))
  ))

  // Increment
  yield* RefSubject.increment(count)
  // Output: "Count changed: 6"
}))
import { Effect } from "effect"
import * as RefSubject from "@typed/fx/RefSubject"

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

  // Later, clean up
  yield* ref.interrupt
})
import { Effect } from "effect"
import * as RefSubject from "@typed/fx/RefSubject"

const program = Effect.gen(function* () {
  const balance = yield* RefSubject.make(100)

  // Transfer money atomically
  yield* balance.updates((ref) =>
    Effect.gen(function* () {
      const current = yield* ref.get
      if (current >= 50) {
        yield* ref.set(current - 50)
        return "Transfer successful"
      }
      return "Insufficient funds"
    })
  )
})

Members

Other public imports

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

Source