Skip to content

Commit

Permalink
Resource<A, E> and ScopedRed<A> is subtype of Effect. (#3626)
Browse files Browse the repository at this point in the history
Co-authored-by: maksim.khramtsov <maksim.khramtsov@btsdigital.kz>
  • Loading branch information
2 people authored and tim-smart committed Oct 6, 2024
1 parent 022c030 commit d12d512
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 16 deletions.
6 changes: 6 additions & 0 deletions .changeset/wicked-bears-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"effect": minor
---

`Resource<A, E>` is subtype of `Effect<A, E>`.
`ScopedRed<A>` is subtype of `Effect<A>`.
21 changes: 20 additions & 1 deletion packages/effect/dtslint/Unify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<never, 2> | Resource<1, 2> | Resource<"a", "b"> | Resource<any, any>
export type ResourceUnify = Unify.Unify<
| Resource.Resource<1>
| Resource.Resource<never, 2>
| Resource.Resource<1, 2>
| Resource.Resource<"a", "b">
| Resource.Resource<any, any>
>

// $ExpectType 0 | Option<string | number> | 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<string | number> | 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">
Expand Down Expand Up @@ -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
>
24 changes: 23 additions & 1 deletion packages/effect/src/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,11 +30,31 @@ export type ResourceTypeId = typeof ResourceTypeId
* @since 2.0.0
* @category models
*/
export interface Resource<in out A, in out E = never> extends Resource.Variance<A, E> {
export interface Resource<in out A, in out E = never> extends Effect.Effect<A, E>, Resource.Variance<A, E>, Pipeable {
/** @internal */
readonly scopedRef: ScopedRef.ScopedRef<Exit.Exit<A, E>>
/** @internal */
readonly acquire: Effect.Effect<A, E, Scope.Scope>

readonly [Unify.typeSymbol]?: unknown
readonly [Unify.unifySymbol]?: ResourceUnify<this>
readonly [Unify.ignoreSymbol]?: ResourceUnifyIgnore
}

/**
* @category models
* @since 3.9.0
*/
export interface ResourceUnify<A extends { [Unify.typeSymbol]?: any }> extends Effect.EffectUnify<A> {
Resource?: () => Extract<A[Unify.typeSymbol], Resource<any, any>>
}

/**
* @category models
* @since 3.9.0
*/
export interface ResourceUnifyIgnore extends Effect.EffectUnifyIgnore {
Effect?: true
}

/**
Expand Down
23 changes: 22 additions & 1 deletion packages/effect/src/ScopedRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -31,9 +32,29 @@ export type ScopedRefTypeId = typeof ScopedRefTypeId
* @since 2.0.0
* @category models
*/
export interface ScopedRef<in out A> extends ScopedRef.Variance<A>, Pipeable {
export interface ScopedRef<in out A> extends Effect.Effect<A>, ScopedRef.Variance<A>, Pipeable {
/** @internal */
readonly ref: Synchronized.SynchronizedRef<readonly [Scope.Scope.Closeable, A]>

readonly [Unify.typeSymbol]?: unknown
readonly [Unify.unifySymbol]?: ScopedRefUnify<this>
readonly [Unify.ignoreSymbol]?: ScopedRefUnifyIgnore
}

/**
* @category models
* @since 3.9.0
*/
export interface ScopedRefUnify<A extends { [Unify.typeSymbol]?: any }> extends Effect.EffectUnify<A> {
ScopedRef?: () => Extract<A[Unify.typeSymbol], ScopedRef<any>>
}

/**
* @category models
* @since 3.9.0
*/
export interface ScopedRefUnifyIgnore extends Effect.EffectUnifyIgnore {
Effect?: true
}

/**
Expand Down
21 changes: 16 additions & 5 deletions packages/effect/src/internal/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -23,6 +24,15 @@ const resourceVariance = {
_A: (_: any) => _
}

/** @internal */
const proto: ThisType<Resource.Resource<any, any>> = {
...effectable.CommitPrototype,
commit() {
return get(this)
},
[ResourceTypeId]: resourceVariance
}

/** @internal */
export const auto = <A, E, R, Out, R2>(
acquire: Effect.Effect<A, E, R>,
Expand All @@ -46,11 +56,12 @@ export const manual = <A, E, R>(
core.flatMap(core.context<R>(), (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 */
Expand Down
20 changes: 12 additions & 8 deletions packages/effect/src/internal/scopedRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -25,6 +25,15 @@ const scopedRefVariance = {
_A: (_: any) => _
}

/** @internal */
const proto: ThisType<ScopedRef.ScopedRef<any>> = {
...effectable.CommitPrototype,
commit() {
return get(this)
},
[ScopedRefTypeId]: scopedRefVariance
}

/** @internal */
const close = <A>(self: ScopedRef.ScopedRef<A>): Effect.Effect<void> =>
core.flatMap(ref.get(self.ref), (tuple) => tuple[0].close(core.exitVoid))
Expand All @@ -41,13 +50,8 @@ export const fromAcquire = <A, E, R>(
core.flatMap((value) =>
circular.makeSynchronized([newScope, value] as const).pipe(
core.flatMap((ref) => {
const scopedRef: ScopedRef.ScopedRef<A> = {
[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)
Expand Down
8 changes: 8 additions & 0 deletions packages/effect/test/Resource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}))
})
6 changes: 6 additions & 0 deletions packages/effect/test/ScopedRef.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}))
})

0 comments on commit d12d512

Please sign in to comment.