diff --git a/.changeset/poor-keys-lick.md b/.changeset/poor-keys-lick.md new file mode 100644 index 0000000000..b53884ed72 --- /dev/null +++ b/.changeset/poor-keys-lick.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +`Micro.EnvRef` and `Micro.Handle` is subtype of `Micro` diff --git a/packages/effect/dtslint/Unify.ts b/packages/effect/dtslint/Unify.ts index 4d0d6037ea..8a122c7e87 100644 --- a/packages/effect/dtslint/Unify.ts +++ b/packages/effect/dtslint/Unify.ts @@ -43,6 +43,14 @@ export type StreamUnify = Unify.Unify< export type MicroUnify = Unify.Unify< Micro.Micro<0, 1, 2> | Micro.Micro<"a", "b", "c"> > +// $ExpectType EnvRef<0 | "a"> +export type MicroEnvRefUnify = Unify.Unify< + Micro.EnvRef<0> | Micro.EnvRef<"a"> +> +// $ExpectType Handle<0 | "a", "b" | 1> +export type MicroHandleUnify = Unify.Unify< + Micro.Handle<0, 1> | Micro.Handle<"a", "b"> +> // $ExpectType Effect<0 | "a", "b" | 1, "c" | 2> export type EffectUnify = Unify.Unify< | Effect.Effect<0, 1, 2> diff --git a/packages/effect/src/Micro.ts b/packages/effect/src/Micro.ts index 67dfaead24..6cf83213b3 100644 --- a/packages/effect/src/Micro.ts +++ b/packages/effect/src/Micro.ts @@ -64,11 +64,11 @@ export type runSymbol = typeof runSymbol */ export interface Micro extends Effect { readonly [TypeId]: Micro.Variance - readonly [runSymbol]: (env: Env, onExit: (exit: MicroExit) => void) => void + [runSymbol](env: Env, onExit: (exit: MicroExit) => void): void + [Symbol.iterator](): MicroIterator> [Unify.typeSymbol]?: unknown [Unify.unifySymbol]?: MicroUnify [Unify.ignoreSymbol]?: MicroUnifyIgnore - [Symbol.iterator](): MicroIterator> } /** @@ -144,6 +144,58 @@ export interface MicroIterator> { next(...args: ReadonlyArray): IteratorResult, Micro.Success> } +/** + * @since 3.8.4 + * @experimental + * @category models + */ +export interface MicroClass { + new(): Micro +} + +// ---------------------------------------------------------------------------- +// Microable +// ---------------------------------------------------------------------------- + +const MicroProto = { + ...Effectable.EffectPrototype, + _op: "Micro", + [TypeId]: { + _A: identity, + _E: identity, + _R: identity + }, + [Symbol.iterator]() { + return new SingleShotGen(new YieldWrap(this)) as any + } +} + +const MicroBase: MicroClass = (function() { + function Base() {} + Base.prototype = MicroProto + return Base as any +})() + +/** + * @since 3.8.4 + * @experimental + * @category constructors + */ +export abstract class Class extends MicroBase { + /** + * @since 3.8.4 + * @experimental + */ + abstract asMicro(): Micro + /** + * @since 3.8.4 + * @experimental + */ + [runSymbol](env: Env, onExit: (exit: MicroExit) => void): void { + this.asMicro()[runSymbol](env, onExit) + } +} + // ---------------------------------------------------------------------------- // MicroCause // ---------------------------------------------------------------------------- @@ -512,31 +564,6 @@ export interface Env extends Pipeable { readonly refs: ReadonlyRecord } -/** - * @since 3.4.0 - * @experimental - * @category environment - */ -export const EnvRefTypeId: unique symbol = Symbol.for("effect/Micro/EnvRef") - -/** - * @since 3.4.0 - * @experimental - * @category environment - */ -export type EnvRefTypeId = typeof EnvRefTypeId - -/** - * @since 3.4.0 - * @experimental - * @category environment - */ -export interface EnvRef { - readonly [EnvRefTypeId]: EnvRefTypeId - readonly key: string - readonly initial: A -} - const EnvProto = { [EnvTypeId]: { _R: identity @@ -818,8 +845,59 @@ export class MicroSchedulerDefault implements MicroScheduler { // Env refs // ======================================================================== -const EnvRefProto = { - [EnvRefTypeId]: EnvRefTypeId +/** + * @since 3.4.0 + * @experimental + * @category environment + */ +export const EnvRefTypeId: unique symbol = Symbol.for("effect/Micro/EnvRef") + +/** + * @since 3.4.0 + * @experimental + * @category environment + */ +export type EnvRefTypeId = typeof EnvRefTypeId + +/** + * @since 3.4.0 + * @experimental + * @category environment + */ +export interface EnvRef extends Micro { + readonly [EnvRefTypeId]: EnvRefTypeId + readonly key: string + readonly initial: A + + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: EnvRefUnify + [Unify.ignoreSymbol]?: EnvRefUnifyIgnore +} + +/** + * @category models + * @since 3.8.4 + * @experimental + */ +export interface EnvRefUnify extends MicroUnify { + EnvRef?: () => A[Unify.typeSymbol] extends EnvRef | infer _ ? EnvRef : never +} + +/** + * @category models + * @since 3.8.4 + * @experimental + */ +export interface EnvRefUnifyIgnore extends MicroUnifyIgnore { + Micro?: true +} + +const EnvRefProto: ThisType> = { + ...MicroProto, + [EnvRefTypeId]: EnvRefTypeId, + [runSymbol](env: Env, onExit: (exit: MicroExit) => void) { + getEnvRef(this)[runSymbol](env, onExit) + } } /** @@ -929,19 +1007,6 @@ export const withConcurrency: { // constructors // ---------------------------------------------------------------------------- -const MicroProto = { - ...Effectable.EffectPrototype, - _op: "Micro", - [TypeId]: { - _A: identity, - _E: identity, - _R: identity - }, - [Symbol.iterator]() { - return new SingleShotGen(new YieldWrap(this)) as any - } -} - const microDepthState = globalValue("effect/Micro/microDepthState", () => ({ depth: 0, maxDepthBeforeYield: currentMaxDepthBeforeYield.initial @@ -3652,7 +3717,7 @@ export type HandleTypeId = typeof HandleTypeId * @experimental * @category handle & forking */ -export interface Handle { +export interface Handle extends Micro { readonly [HandleTypeId]: HandleTypeId readonly await: Micro> readonly join: Micro @@ -3661,6 +3726,28 @@ export interface Handle { readonly addObserver: (observer: (exit: MicroExit) => void) => void readonly removeObserver: (observer: (exit: MicroExit) => void) => void readonly unsafePoll: () => MicroExit | null + + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: HandleUnify + [Unify.ignoreSymbol]?: HandleUnifyIgnore +} + +/** + * @category models + * @since 3.8.4 + * @experimental + */ +export interface HandleUnify extends MicroUnify { + Handle?: () => A[Unify.typeSymbol] extends Handle | infer _ ? Handle : never +} + +/** + * @category models + * @since 3.8.4 + * @experimental + */ +export interface HandleUnifyIgnore extends MicroUnifyIgnore { + Micro?: true } /** @@ -3671,7 +3758,7 @@ export interface Handle { export const isHandle = (u: unknown): u is Handle => typeof u === "object" && u !== null && HandleTypeId in u -class HandleImpl implements Handle { +class HandleImpl extends Class implements Handle { readonly [HandleTypeId]: HandleTypeId readonly observers: Set<(exit: MicroExit) => void> = new Set() @@ -3680,6 +3767,7 @@ class HandleImpl implements Handle { readonly isRoot: boolean constructor(readonly parentSignal: AbortSignal, controller?: AbortController) { + super() this[HandleTypeId] = HandleTypeId this.isRoot = controller !== undefined this._controller = controller ?? new AbortController() @@ -3746,6 +3834,10 @@ class HandleImpl implements Handle { return this.await }) } + + asMicro(): Micro { + return this.join + } } /** diff --git a/packages/effect/test/Micro.test.ts b/packages/effect/test/Micro.test.ts index d040d81967..268bfabdb7 100644 --- a/packages/effect/test/Micro.test.ts +++ b/packages/effect/test/Micro.test.ts @@ -331,7 +331,7 @@ describe.concurrent("Micro", () => { it.effect("raceAll", () => Micro.gen(function*() { const interrupted: Array = [] - const result = yield* Micro.raceAll([100, 75, 50, 0, 25].map((ms) => + const result = yield* Micro.raceAll([500, 300, 200, 0, 100].map((ms) => (ms === 0 ? Micro.fail("boom") : Micro.succeed(ms)).pipe( Micro.delay(ms), Micro.onInterrupt( @@ -341,14 +341,14 @@ describe.concurrent("Micro", () => { ) ) )) - assert.strictEqual(result, 25) - assert.deepStrictEqual(interrupted, [100, 75, 50]) + assert.strictEqual(result, 100) + assert.deepStrictEqual(interrupted, [500, 300, 200]) })) it("raceAllFirst", () => Micro.gen(function*() { const interrupted: Array = [] - const result = yield* Micro.raceAllFirst([100, 75, 50, 0, 25].map((ms) => + const result = yield* Micro.raceAllFirst([500, 300, 200, 0, 100].map((ms) => (ms === 0 ? Micro.fail("boom") : Micro.succeed(ms)).pipe( Micro.delay(ms), Micro.onInterrupt( @@ -359,7 +359,7 @@ describe.concurrent("Micro", () => { ) )).pipe(Micro.exit) assert.deepStrictEqual(result, Micro.exitFail("boom")) - assert.deepStrictEqual(interrupted, [100, 75, 50, 25]) + assert.deepStrictEqual(interrupted, [500, 300, 200, 100]) }).pipe(Micro.runPromise)) describe("valid Effect", () => { @@ -1193,4 +1193,29 @@ describe.concurrent("Micro", () => { assert.deepStrictEqual(out, [100, 100, 100, 160, 320, 640, 1280]) }) }) + + describe("Handle", () => { + it("is subtype of Micro: (Success)", () => + Micro.gen(function*() { + const handle = yield* Micro.succeed(1).pipe(Micro.fork) + const res = yield* handle + assert.equal(res, 1) + }).pipe(Micro.runPromise)) + + it("is subtype of Micro: (Failure)", () => + Micro.gen(function*() { + const handle = yield* Micro.fail(1).pipe(Micro.fork) + const res = yield* Micro.flip(handle) + assert.equal(res, 1) + }).pipe(Micro.runPromise)) + }) + + describe("EnvRef", () => { + it("is subtype of Micro", () => + Micro.gen(function*() { + const envRef = Micro.envRefMake("test", () => 1) + const res = yield* envRef + assert.equal(res, 1) + }).pipe(Micro.runPromise)) + }) })