From 551764417adc059d06f65c5a0c0f99c8d71db365 Mon Sep 17 00:00:00 2001 From: Maxim Khramtsov Date: Thu, 19 Sep 2024 03:56:57 +0200 Subject: [PATCH] `Resource` and `ScopedRed` is subtype of `Effect`. (#3626) Co-authored-by: maksim.khramtsov --- .changeset/wicked-bears-flow.md | 6 ++++++ packages/effect/dtslint/Unify.ts | 21 +++++++++++++++++++- packages/effect/src/Resource.ts | 24 ++++++++++++++++++++++- packages/effect/src/ScopedRef.ts | 23 +++++++++++++++++++++- packages/effect/src/internal/resource.ts | 21 +++++++++++++++----- packages/effect/src/internal/scopedRef.ts | 20 +++++++++++-------- packages/effect/test/Resource.test.ts | 8 ++++++++ packages/effect/test/ScopedRef.test.ts | 6 ++++++ 8 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 .changeset/wicked-bears-flow.md diff --git a/.changeset/wicked-bears-flow.md b/.changeset/wicked-bears-flow.md new file mode 100644 index 0000000000..1781cd7b07 --- /dev/null +++ b/.changeset/wicked-bears-flow.md @@ -0,0 +1,6 @@ +--- +"effect": minor +--- + +`Resource` is subtype of `Effect`. +`ScopedRed` is subtype of `Effect`. diff --git a/packages/effect/dtslint/Unify.ts b/packages/effect/dtslint/Unify.ts index 8a122c7e87..926da4eb36 100644 --- a/packages/effect/dtslint/Unify.ts +++ b/packages/effect/dtslint/Unify.ts @@ -9,6 +9,8 @@ import type * as Option from "effect/Option" import type * as Queue from "effect/Queue" import type * as RcRef from "effect/RcRef" import type * as Ref from "effect/Ref" +import type * as Resource from "effect/Resource" +import type * as ScopedRef from "effect/ScopedRef" import type * as Stream from "effect/Stream" import type * as SubscriptionRef from "effect/SubscriptionRef" import type * as SynchronizedRef from "effect/SynchronizedRef" @@ -109,8 +111,21 @@ export type DequeueUnify = Unify.Unify< | Queue.Dequeue<1> | Queue.Dequeue<"a"> > +// $ExpectType ScopedRef<1> | ScopedRef<"a"> +export type ScopedRefUnify = Unify.Unify< + | ScopedRef.ScopedRef<1> + | ScopedRef.ScopedRef<"a"> +> +// $ExpectType Resource<1, never> | Resource | Resource<1, 2> | Resource<"a", "b"> | Resource +export type ResourceUnify = Unify.Unify< + | Resource.Resource<1> + | Resource.Resource + | Resource.Resource<1, 2> + | Resource.Resource<"a", "b"> + | Resource.Resource +> -// $ExpectType 0 | Option | Ref<1> | SynchronizedRef<1> | SubscriptionRef<1> | Deferred<1, 2> | Deferred<"a", "b"> | Fiber<"a" | 1, "b" | 2> | RuntimeFiber<"a" | 1, "b" | 2> | Queue<1> | Queue<"a"> | Dequeue<"a" | 1> | Ref<"A"> | SynchronizedRef<"A"> | SubscriptionRef<"A"> | FiberRef<12> | FiberRef<"a2"> | Either<1 | "A", 0 | "E"> | Effect<1 | "A", 0 | "E", "R" | "R1"> | RcRef<1 | "A", 0 | "E"> +// $ExpectType 0 | Option | Ref<1> | SynchronizedRef<1> | SubscriptionRef<1> | Deferred<1, 2> | Deferred<"a", "b"> | Fiber<"a" | 1, "b" | 2> | RuntimeFiber<"a" | 1, "b" | 2> | Queue<1> | Queue<"a"> | Dequeue<"a" | 1> | ScopedRef<1> | ScopedRef<"a"> | Resource<1, 2> | Ref<"A"> | SynchronizedRef<"A"> | SubscriptionRef<"A"> | FiberRef<12> | FiberRef<"a2"> | Resource<"a", never> | 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"> @@ -138,5 +153,9 @@ export type AllUnify = Unify.Unify< | Queue.Queue<"a"> | Queue.Dequeue<1> | Queue.Dequeue<"a"> + | ScopedRef.ScopedRef<1> + | ScopedRef.ScopedRef<"a"> + | Resource.Resource<1, 2> + | Resource.Resource<"a"> | 0 > diff --git a/packages/effect/src/Resource.ts b/packages/effect/src/Resource.ts index dc9af2a864..8dc6debd34 100644 --- a/packages/effect/src/Resource.ts +++ b/packages/effect/src/Resource.ts @@ -4,10 +4,12 @@ import type * as Effect from "./Effect.js" import type * as Exit from "./Exit.js" import * as internal from "./internal/resource.js" +import type { Pipeable } from "./Pipeable.js" import type * as Schedule from "./Schedule.js" import type * as Scope from "./Scope.js" import type * as ScopedRef from "./ScopedRef.js" import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" /** * @since 2.0.0 @@ -28,11 +30,31 @@ export type ResourceTypeId = typeof ResourceTypeId * @since 2.0.0 * @category models */ -export interface Resource extends Resource.Variance { +export interface Resource extends Effect.Effect, Resource.Variance, Pipeable { /** @internal */ readonly scopedRef: ScopedRef.ScopedRef> /** @internal */ readonly acquire: Effect.Effect + + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: ResourceUnify + readonly [Unify.ignoreSymbol]?: ResourceUnifyIgnore +} + +/** + * @category models + * @since 3.9.0 + */ +export interface ResourceUnify extends Effect.EffectUnify { + Resource?: () => Extract> +} + +/** + * @category models + * @since 3.9.0 + */ +export interface ResourceUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true } /** diff --git a/packages/effect/src/ScopedRef.ts b/packages/effect/src/ScopedRef.ts index cc46efb205..8d44654afb 100644 --- a/packages/effect/src/ScopedRef.ts +++ b/packages/effect/src/ScopedRef.ts @@ -8,6 +8,7 @@ import type { Pipeable } from "./Pipeable.js" import type * as Scope from "./Scope.js" import type * as Synchronized from "./SynchronizedRef.js" import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" /** * @since 2.0.0 @@ -31,9 +32,29 @@ export type ScopedRefTypeId = typeof ScopedRefTypeId * @since 2.0.0 * @category models */ -export interface ScopedRef extends ScopedRef.Variance, Pipeable { +export interface ScopedRef extends Effect.Effect, ScopedRef.Variance, Pipeable { /** @internal */ readonly ref: Synchronized.SynchronizedRef + + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: ScopedRefUnify + readonly [Unify.ignoreSymbol]?: ScopedRefUnifyIgnore +} + +/** + * @category models + * @since 3.9.0 + */ +export interface ScopedRefUnify extends Effect.EffectUnify { + ScopedRef?: () => Extract> +} + +/** + * @category models + * @since 3.9.0 + */ +export interface ScopedRefUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true } /** diff --git a/packages/effect/src/internal/resource.ts b/packages/effect/src/internal/resource.ts index 74a21dc314..bd1bf5e706 100644 --- a/packages/effect/src/internal/resource.ts +++ b/packages/effect/src/internal/resource.ts @@ -4,6 +4,7 @@ import type * as Resource from "../Resource.js" import type * as Schedule from "../Schedule.js" import type * as Scope from "../Scope.js" import * as core from "./core.js" +import * as effectable from "./effectable.js" import * as fiberRuntime from "./fiberRuntime.js" import * as _schedule from "./schedule.js" import * as scopedRef from "./scopedRef.js" @@ -23,6 +24,15 @@ const resourceVariance = { _A: (_: any) => _ } +/** @internal */ +const proto: ThisType> = { + ...effectable.CommitPrototype, + commit() { + return get(this) + }, + [ResourceTypeId]: resourceVariance +} + /** @internal */ export const auto = ( acquire: Effect.Effect, @@ -46,11 +56,12 @@ export const manual = ( core.flatMap(core.context(), (env) => pipe( scopedRef.fromAcquire(core.exit(acquire)), - core.map((ref) => ({ - [ResourceTypeId]: resourceVariance, - scopedRef: ref, - acquire: core.provideContext(acquire, env) - })) + core.map((ref) => { + const resource = Object.create(proto) + resource.scopedRef = ref + resource.acquire = core.provideContext(acquire, env) + return resource + }) )) /** @internal */ diff --git a/packages/effect/src/internal/scopedRef.ts b/packages/effect/src/internal/scopedRef.ts index 2e588c36b7..dca8d7d481 100644 --- a/packages/effect/src/internal/scopedRef.ts +++ b/packages/effect/src/internal/scopedRef.ts @@ -2,11 +2,11 @@ import * as Context from "../Context.js" import type * as Effect from "../Effect.js" import type { LazyArg } from "../Function.js" import { dual, pipe } from "../Function.js" -import { pipeArguments } from "../Pipeable.js" import type * as Scope from "../Scope.js" import type * as ScopedRef from "../ScopedRef.js" import * as core from "./core.js" import * as circular from "./effect/circular.js" +import * as effectable from "./effectable.js" import * as fiberRuntime from "./fiberRuntime.js" import * as ref from "./ref.js" import * as synchronized from "./synchronizedRef.js" @@ -25,6 +25,15 @@ const scopedRefVariance = { _A: (_: any) => _ } +/** @internal */ +const proto: ThisType> = { + ...effectable.CommitPrototype, + commit() { + return get(this) + }, + [ScopedRefTypeId]: scopedRefVariance +} + /** @internal */ const close = (self: ScopedRef.ScopedRef): Effect.Effect => core.flatMap(ref.get(self.ref), (tuple) => tuple[0].close(core.exitVoid)) @@ -41,13 +50,8 @@ export const fromAcquire = ( core.flatMap((value) => circular.makeSynchronized([newScope, value] as const).pipe( core.flatMap((ref) => { - const scopedRef: ScopedRef.ScopedRef = { - [ScopedRefTypeId]: scopedRefVariance, - pipe() { - return pipeArguments(this, arguments) - }, - ref - } + const scopedRef = Object.create(proto) + scopedRef.ref = ref return pipe( fiberRuntime.addFinalizer(() => close(scopedRef)), core.as(scopedRef) diff --git a/packages/effect/test/Resource.test.ts b/packages/effect/test/Resource.test.ts index 5043b9530c..a2b7af50f5 100644 --- a/packages/effect/test/Resource.test.ts +++ b/packages/effect/test/Resource.test.ts @@ -51,4 +51,12 @@ describe("Resource", () => { assert.strictEqual(result1, 0) assert.strictEqual(result2, 0) })) + it.scoped("subtype of Effect", () => + Effect.gen(function*() { + const ref = yield* Ref.make(0) + const cached = yield* Cached.manual(ref) + const resul1 = yield* cached + + assert.strictEqual(resul1, 0) + })) }) diff --git a/packages/effect/test/ScopedRef.test.ts b/packages/effect/test/ScopedRef.test.ts index fcd55f6100..f4c942c922 100644 --- a/packages/effect/test/ScopedRef.test.ts +++ b/packages/effect/test/ScopedRef.test.ts @@ -79,4 +79,10 @@ describe("ScopedRef", () => { const ref = yield* _(Effect.scoped(ScopedRef.make(() => 0))) expect(ref.pipe(identity)).toBe(ref) })) + it.scoped("subtype of Effect", () => + Effect.gen(function*() { + const ref = yield* ScopedRef.make(() => 0) + const result = yield* ref + assert.strictEqual(result, 0) + })) })