diff --git a/.changeset/eighty-lobsters-refuse.md b/.changeset/eighty-lobsters-refuse.md new file mode 100644 index 00000000000..571aff3b769 --- /dev/null +++ b/.changeset/eighty-lobsters-refuse.md @@ -0,0 +1,5 @@ +--- +"effect": minor +--- + +Made `Ref`, `SynchronizedRed` and `SubscriptionRef` a subtype of `Effect` diff --git a/packages/effect/dtslint/Unify.ts b/packages/effect/dtslint/Unify.ts index 4aabe4784d7..b5d13be1564 100644 --- a/packages/effect/dtslint/Unify.ts +++ b/packages/effect/dtslint/Unify.ts @@ -1,18 +1,23 @@ import type * as Effect from "effect/Effect" import * as Either from "effect/Either" +import type * as Exit from "effect/Exit" import type * as Micro from "effect/Micro" import type * as Option from "effect/Option" +import type * as RcRef from "effect/RcRef" +import type * as Ref from "effect/Ref" import type * as Stream from "effect/Stream" +import type * as SubscriptionRef from "effect/SubscriptionRef" +import type * as SynchronizedRef from "effect/SynchronizedRef" import * as Unify from "effect/Unify" // $ExpectType Option -export type option = Unify.Unify | Option.Option> +export type OptionUnify = Unify.Unify | Option.Option> // $ExpectType Either<"RA" | "RB", "LA" | "LB"> -export type either = Unify.Unify | Either.Either<"RB", "LB">> +export type EitherUnify = Unify.Unify | Either.Either<"RB", "LB">> // $ExpectType 0 | Option | Either<"RA" | "RB", "LA" | "LB"> -export type both = Unify.Unify< +export type EitherOptionUnify = Unify.Unify< Either.Either<"RA", "LA"> | Either.Either<"RB", "LB"> | Option.Option | Option.Option | 0 > @@ -26,15 +31,57 @@ Unify.unify((n: N) => Math.random() > 0 ? Either.right(n) : Either.left("ok") Unify.unify(Math.random() > 0 ? Either.right(10) : Either.left("ok")) // $ExpectType Stream<0 | "a", "b" | 1, "c" | 2> -export type SU = Unify.Unify< +export type StreamUnify = Unify.Unify< Stream.Stream<0, 1, 2> | Stream.Stream<"a", "b", "c"> > // $ExpectType Micro<0 | "a", "b" | 1, "c" | 2> -export type MU = Unify.Unify< +export type MicroUnify = Unify.Unify< Micro.Micro<0, 1, 2> | Micro.Micro<"a", "b", "c"> > // $ExpectType Effect<0 | "a", "b" | 1, "c" | 2> -export type EU = Unify.Unify< - Effect.Effect<0, 1, 2> | Effect.Effect<"a", "b", "c"> +export type EffectUnify = Unify.Unify< + | Effect.Effect<0, 1, 2> + | Effect.Effect<"a", "b", "c"> +> +// $ExpectType Exit<0 | "a", "b" | 1> +export type ExitUnify = Unify.Unify< + | Exit.Exit<0, 1> + | Exit.Exit<"a", "b"> +> +// $ExpectType Ref<1> | Ref<"a"> +export type RefUnify = Unify.Unify | Ref.Ref<"a">> +// $ExpectType SynchronizedRef<1> | SynchronizedRef<"a"> +export type SynchronizedRefUnify = Unify.Unify< + | SynchronizedRef.SynchronizedRef<1> + | SynchronizedRef.SynchronizedRef<"a"> +> +// $ExpectType SubscriptionRef<1> | SubscriptionRef<"a"> +export type SubscriptionRefUnify = Unify.Unify< + | SubscriptionRef.SubscriptionRef<1> + | SubscriptionRef.SubscriptionRef<"a"> +> +// $ExpectType RcRef<"a" | 1, "b" | 2> +export type RcRefUnify = Unify.Unify< + | RcRef.RcRef<1, 2> + | RcRef.RcRef<"a", "b"> +> + +// $ExpectType 0 | Option | Ref<1> | SynchronizedRef<1> | SubscriptionRef<1> | Ref<"A"> | SynchronizedRef<"A"> | SubscriptionRef<"A"> | Either<1 | "A", 0 | "E"> | Effect<1 | "A", 0 | "E", "R" | "R1"> | RcRef<1 | "A", 0 | "E"> +export type AllUnify = Unify.Unify< + | Either.Either<1, 0> + | Either.Either<"A", "E"> + | Option.Option + | Option.Option + | Effect.Effect<"A", "E", "R"> + | Effect.Effect<1, 0, "R1"> + | Ref.Ref<1> + | Ref.Ref<"A"> + | SynchronizedRef.SynchronizedRef<1> + | SynchronizedRef.SynchronizedRef<"A"> + | SubscriptionRef.SubscriptionRef<1> + | SubscriptionRef.SubscriptionRef<"A"> + | RcRef.RcRef<1, 0> + | RcRef.RcRef<"A", "E"> + | 0 > diff --git a/packages/effect/src/RcRef.ts b/packages/effect/src/RcRef.ts index bbdf780a5eb..e014272d9fe 100644 --- a/packages/effect/src/RcRef.ts +++ b/packages/effect/src/RcRef.ts @@ -4,9 +4,10 @@ import type * as Duration from "./Duration.js" import type * as Effect from "./Effect.js" import * as internal from "./internal/rcRef.js" -import { type Pipeable } from "./Pipeable.js" +import type * as Readable from "./Readable.js" import type * as Scope from "./Scope.js" import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" /** * @since 3.5.0 @@ -24,10 +25,31 @@ export type TypeId = typeof TypeId * @since 3.5.0 * @category models */ -export interface RcRef extends Pipeable { +export interface RcRef + extends Effect.Effect, Readable.Readable +{ readonly [TypeId]: RcRef.Variance + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: RcRefUnify + readonly [Unify.ignoreSymbol]?: RcRefUnifyIgnore } +/** + * @category models + * @since 3.8.0 + */ +export interface RcRefUnify extends Effect.EffectUnify { + RcRef?: () => A[Unify.typeSymbol] extends RcRef | infer _ ? RcRef + : never +} + +/** + * @category models + * @since 3.8.0 + */ +export interface RcRefUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true +} /** * @since 3.5.0 * @category models diff --git a/packages/effect/src/Ref.ts b/packages/effect/src/Ref.ts index 09be40ce7ba..0f63baef06c 100644 --- a/packages/effect/src/Ref.ts +++ b/packages/effect/src/Ref.ts @@ -4,8 +4,9 @@ import type * as Effect from "./Effect.js" import * as internal from "./internal/ref.js" import type * as Option from "./Option.js" -import type { Readable } from "./Readable.js" +import type * as Readable from "./Readable.js" import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" /** * @since 2.0.0 @@ -23,8 +24,27 @@ export type RefTypeId = typeof RefTypeId * @since 2.0.0 * @category models */ -export interface Ref extends Ref.Variance, Readable { +export interface Ref extends Ref.Variance, Effect.Effect, Readable.Readable { modify(f: (a: A) => readonly [B, A]): Effect.Effect + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: RefUnify + readonly [Unify.ignoreSymbol]?: RefUnifyIgnore +} + +/** + * @category models + * @since 3.8.0 + */ +export interface RefUnify extends Effect.EffectUnify { + Ref?: () => Extract> +} + +/** + * @category models + * @since 3.8.0 + */ +export interface RefUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true } /** diff --git a/packages/effect/src/SubscriptionRef.ts b/packages/effect/src/SubscriptionRef.ts index 36850a864ec..aac1045a5b3 100644 --- a/packages/effect/src/SubscriptionRef.ts +++ b/packages/effect/src/SubscriptionRef.ts @@ -10,6 +10,7 @@ import type * as Stream from "./Stream.js" import type { Subscribable } from "./Subscribable.js" import * as Synchronized from "./SynchronizedRef.js" import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" /** * @since 2.0.0 @@ -44,6 +45,27 @@ export interface SubscriptionRef * to that value. */ readonly changes: Stream.Stream + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: SubscriptionRefUnify + readonly [Unify.ignoreSymbol]?: SubscriptionRefUnifyIgnore +} + +/** + * @category models + * @since 3.8.0 + */ +export interface SubscriptionRefUnify + extends Synchronized.SynchronizedRefUnify +{ + SubscriptionRef?: () => Extract> +} + +/** + * @category models + * @since 3.8.0 + */ +export interface SubscriptionRefUnifyIgnore extends Synchronized.SynchronizedRefUnifyIgnore { + SynchronizedRef?: true } /** diff --git a/packages/effect/src/SynchronizedRef.ts b/packages/effect/src/SynchronizedRef.ts index a125fb8202f..cf763ec0ba7 100644 --- a/packages/effect/src/SynchronizedRef.ts +++ b/packages/effect/src/SynchronizedRef.ts @@ -8,6 +8,7 @@ import * as internal from "./internal/synchronizedRef.js" import type * as Option from "./Option.js" import type * as Ref from "./Ref.js" import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" /** * @since 2.0.0 @@ -27,6 +28,25 @@ export type SynchronizedRefTypeId = typeof SynchronizedRefTypeId */ export interface SynchronizedRef extends SynchronizedRef.Variance, Ref.Ref { modifyEffect(f: (a: A) => Effect.Effect): Effect.Effect + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: SynchronizedRefUnify + readonly [Unify.ignoreSymbol]?: SynchronizedRefUnifyIgnore +} + +/** + * @category models + * @since 3.8.0 + */ +export interface SynchronizedRefUnify extends Ref.RefUnify { + SynchronizedRef?: () => Extract> +} + +/** + * @category models + * @since 3.8.0 + */ +export interface SynchronizedRefUnifyIgnore extends Ref.RefUnifyIgnore { + Ref?: true } /** diff --git a/packages/effect/src/internal/effect/circular.ts b/packages/effect/src/internal/effect/circular.ts index 5a3fdbd3c16..da384e54578 100644 --- a/packages/effect/src/internal/effect/circular.ts +++ b/packages/effect/src/internal/effect/circular.ts @@ -2,6 +2,7 @@ import type * as Cause from "../../Cause.js" import type * as Deferred from "../../Deferred.js" import * as Duration from "../../Duration.js" import type * as Effect from "../../Effect.js" +import * as Effectable from "../../Effectable.js" import * as Equal from "../../Equal.js" import type { Equivalence } from "../../Equivalence.js" import * as Exit from "../../Exit.js" @@ -571,18 +572,21 @@ export const synchronizedVariance = { } /** @internal */ -class SynchronizedImpl implements Synchronized.SynchronizedRef { +class SynchronizedImpl extends Effectable.Class implements Synchronized.SynchronizedRef { readonly [SynchronizedTypeId] = synchronizedVariance readonly [internalRef.RefTypeId] = internalRef.refVariance - readonly [Readable.TypeId]: Readable.TypeId + readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId constructor( readonly ref: Ref.Ref, readonly withLock: (self: Effect.Effect) => Effect.Effect ) { - this[Readable.TypeId] = Readable.TypeId + super() this.get = internalRef.get(this.ref) } readonly get: Effect.Effect + commit() { + return this.get + } modify(f: (a: A) => readonly [B, A]): Effect.Effect { return this.modifyEffect((a) => core.succeed(f(a))) } @@ -594,9 +598,6 @@ class SynchronizedImpl implements Synchronized.SynchronizedRef { ) ) } - pipe() { - return pipeArguments(this, arguments) - } } /** @internal */ diff --git a/packages/effect/src/internal/rcRef.ts b/packages/effect/src/internal/rcRef.ts index 8fe9841397d..daf0b0b88ec 100644 --- a/packages/effect/src/internal/rcRef.ts +++ b/packages/effect/src/internal/rcRef.ts @@ -1,10 +1,11 @@ import * as Context from "../Context.js" import * as Duration from "../Duration.js" import type { Effect } from "../Effect.js" +import * as Effectable from "../Effectable.js" import type { RuntimeFiber } from "../Fiber.js" import { identity } from "../Function.js" -import { pipeArguments } from "../Pipeable.js" import type * as RcRef from "../RcRef.js" +import * as Readable from "../Readable.js" import type * as Scope from "../Scope.js" import * as coreEffect from "./core-effect.js" import * as core from "./core.js" @@ -42,8 +43,9 @@ const variance: RcRef.RcRef.Variance = { _E: identity } -class RcRefImpl implements RcRef.RcRef { - readonly [TypeId]: RcRef.RcRef.Variance +class RcRefImpl extends Effectable.Class implements RcRef.RcRef { + readonly [TypeId]: RcRef.RcRef.Variance = variance + readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId state: State = stateEmpty readonly semaphore = circular.unsafeMakeSemaphore(1) @@ -54,11 +56,13 @@ class RcRefImpl implements RcRef.RcRef { readonly scope: Scope.Scope, readonly idleTimeToLive: Duration.Duration | undefined ) { - this[TypeId] = variance + super() + this.get = get(this) } + readonly get: Effect - pipe() { - return pipeArguments(this, arguments) + commit() { + return this.get } } diff --git a/packages/effect/src/internal/ref.ts b/packages/effect/src/internal/ref.ts index 666122b8be8..cc10f6b3e01 100644 --- a/packages/effect/src/internal/ref.ts +++ b/packages/effect/src/internal/ref.ts @@ -1,8 +1,8 @@ import type * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" import { dual } from "../Function.js" import * as MutableRef from "../MutableRef.js" import * as Option from "../Option.js" -import { pipeArguments } from "../Pipeable.js" import * as Readable from "../Readable.js" import type * as Ref from "../Ref.js" import * as core from "./core.js" @@ -16,11 +16,14 @@ export const refVariance = { _A: (_: any) => _ } -class RefImpl implements Ref.Ref { +class RefImpl extends Effectable.Class implements Ref.Ref { + commit() { + return this.get + } readonly [RefTypeId] = refVariance - readonly [Readable.TypeId]: Readable.TypeId + readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId constructor(readonly ref: MutableRef.MutableRef) { - this[Readable.TypeId] = Readable.TypeId + super() this.get = core.sync(() => MutableRef.get(this.ref)) } readonly get: Effect.Effect @@ -34,9 +37,6 @@ class RefImpl implements Ref.Ref { return b }) } - pipe() { - return pipeArguments(this, arguments) - } } /** @internal */ diff --git a/packages/effect/src/internal/subscriptionRef.ts b/packages/effect/src/internal/subscriptionRef.ts index e9d08ea672d..9b7ad1dec30 100644 --- a/packages/effect/src/internal/subscriptionRef.ts +++ b/packages/effect/src/internal/subscriptionRef.ts @@ -1,6 +1,6 @@ import * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" import { dual, pipe } from "../Function.js" -import { pipeArguments } from "../Pipeable.js" import * as PubSub from "../PubSub.js" import * as Readable from "../Readable.js" import * as Ref from "../Ref.js" @@ -26,9 +26,9 @@ const subscriptionRefVariance = { } /** @internal */ -class SubscriptionRefImpl implements SubscriptionRef.SubscriptionRef { - readonly [Readable.TypeId]: Readable.TypeId - readonly [Subscribable.TypeId]: Subscribable.TypeId +class SubscriptionRefImpl extends Effectable.Class implements SubscriptionRef.SubscriptionRef { + readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId + readonly [Subscribable.TypeId]: Subscribable.TypeId = Subscribable.TypeId readonly [Ref.RefTypeId] = _ref.refVariance readonly [Synchronized.SynchronizedRefTypeId] = _circular.synchronizedVariance readonly [SubscriptionRefTypeId] = subscriptionRefVariance @@ -37,12 +37,11 @@ class SubscriptionRefImpl implements SubscriptionRef.SubscriptionRef, readonly semaphore: Effect.Semaphore ) { - this[Readable.TypeId] = Readable.TypeId - this[Subscribable.TypeId] = Subscribable.TypeId + super() this.get = Ref.get(this.ref) } - pipe() { - return pipeArguments(this, arguments) + commit() { + return this.get } readonly get: Effect.Effect get changes(): Stream { diff --git a/packages/effect/test/RcRef.test.ts b/packages/effect/test/RcRef.test.ts index c3c439cf2be..02cab8c20b6 100644 --- a/packages/effect/test/RcRef.test.ts +++ b/packages/effect/test/RcRef.test.ts @@ -23,14 +23,14 @@ describe("RcRef", () => { ) assert.strictEqual(acquired, 0) - assert.strictEqual(yield* Effect.scoped(RcRef.get(ref)), "foo") + assert.strictEqual(yield* Effect.scoped(ref), "foo") assert.strictEqual(acquired, 1) assert.strictEqual(released, 1) const scopeA = yield* Scope.make() const scopeB = yield* Scope.make() - yield* RcRef.get(ref).pipe(Scope.extend(scopeA)) - yield* RcRef.get(ref).pipe(Scope.extend(scopeB)) + yield* ref.pipe(Scope.extend(scopeA)) + yield* ref.pipe(Scope.extend(scopeB)) assert.strictEqual(acquired, 2) assert.strictEqual(released, 1) yield* Scope.close(scopeB, Exit.void) @@ -41,7 +41,7 @@ describe("RcRef", () => { assert.strictEqual(released, 2) const scopeC = yield* Scope.make() - yield* RcRef.get(ref).pipe(Scope.extend(scopeC)) + yield* ref.pipe(Scope.extend(scopeC)) assert.strictEqual(acquired, 3) assert.strictEqual(released, 2) @@ -49,7 +49,7 @@ describe("RcRef", () => { assert.strictEqual(acquired, 3) assert.strictEqual(released, 3) - const exit = yield* RcRef.get(ref).pipe(Effect.scoped, Effect.exit) + const exit = yield* ref.get.pipe(Effect.scoped, Effect.exit) assert.isTrue(Exit.isInterrupted(exit)) })) diff --git a/packages/effect/test/Ref.test.ts b/packages/effect/test/Ref.test.ts index 45bb5639870..92ddfeddebe 100644 --- a/packages/effect/test/Ref.test.ts +++ b/packages/effect/test/Ref.test.ts @@ -35,7 +35,7 @@ describe("Ref", () => { Effect.gen(function*(_) { const ref = yield* _(Ref.make(123)) assert.isTrue(Readable.isReadable(ref)) - assert.strictEqual(yield* _(ref.get), 123) + assert.strictEqual(yield* ref, 123) })) it.effect("get", () => @@ -44,10 +44,11 @@ describe("Ref", () => { assert.strictEqual(result, current) })) it.effect("getAndSet", () => - Effect.gen(function*($) { - const ref = yield* $(Ref.make(current)) - const result1 = yield* $(Ref.getAndSet(ref, update)) - const result2 = yield* $(Ref.get(ref)) + Effect.gen(function*() { + const ref = yield* Ref.make(current) + const result1 = yield* Ref.getAndSet(ref, update) + + const result2 = yield* ref assert.strictEqual(result1, current) assert.strictEqual(result2, update) })) @@ -55,7 +56,7 @@ describe("Ref", () => { Effect.gen(function*($) { const ref = yield* $(Ref.make(current)) const result1 = yield* $(Ref.getAndUpdate(ref, () => update)) - const result2 = yield* $(Ref.get(ref)) + const result2 = yield* ref assert.strictEqual(result1, current) assert.strictEqual(result2, update) })) diff --git a/packages/effect/test/Synchronized.test.ts b/packages/effect/test/Synchronized.test.ts index dec353b1985..80f98d743c1 100644 --- a/packages/effect/test/Synchronized.test.ts +++ b/packages/effect/test/Synchronized.test.ts @@ -40,10 +40,10 @@ describe("SynchronizedRef", () => { assert.strictEqual(result, current) })) it.effect("getAndUpdateEffect - happy path", () => - Effect.gen(function*($) { - const ref = yield* $(Synchronized.make(current)) - const result1 = yield* $(Synchronized.getAndUpdateEffect(ref, () => Effect.succeed(update))) - const result2 = yield* $(Synchronized.get(ref)) + Effect.gen(function*() { + const ref = yield* Synchronized.make(current) + const result1 = yield* Synchronized.getAndUpdateEffect(ref, () => Effect.succeed(update)) + const result2 = yield* ref assert.strictEqual(result1, current) assert.strictEqual(result2, update) })) @@ -77,7 +77,7 @@ describe("SynchronizedRef", () => { : isChanged(state) ? Option.some(Effect.succeed(Closed)) : Option.none())) - const result3 = yield* $(Synchronized.get(ref)) + const result3 = yield* ref assert.deepStrictEqual(result1, Active) assert.deepStrictEqual(result2, Changed) assert.deepStrictEqual(result3, Closed)