From 2c80d2b0496e17cc0f969da8fd0a949a015b84fe Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 28 Jul 2023 09:31:28 +1200 Subject: [PATCH 1/5] optional AbortSignal apis using Function.length --- .changeset/nasty-forks-design.md | 5 ++ src/Effect.ts | 98 +++++++--------------- src/internal/core.ts | 53 ++++++++---- src/internal/effect.ts | 140 ++++++++++++++----------------- src/internal/fiberRuntime.ts | 4 +- test/Effect/interruption.ts | 4 +- 6 files changed, 136 insertions(+), 168 deletions(-) create mode 100644 .changeset/nasty-forks-design.md diff --git a/.changeset/nasty-forks-design.md b/.changeset/nasty-forks-design.md new file mode 100644 index 00000000..771c630e --- /dev/null +++ b/.changeset/nasty-forks-design.md @@ -0,0 +1,5 @@ +--- +"@effect/io": minor +--- + +optional AbortSignal apis - remove \*Interrupt variants diff --git a/src/Effect.ts b/src/Effect.ts index b79cb715..99bd78fb 100644 --- a/src/Effect.ts +++ b/src/Effect.ts @@ -897,10 +897,17 @@ export const validateFirst: { * @since 1.0.0 * @category constructors */ -export const async: ( - register: (callback: (_: Effect) => void) => void | Effect, - blockingOn?: FiberId.FiberId -) => Effect = core.async +export const async: { + ( + register: (callback: (_: Effect) => void) => Effect, + blockingOn?: FiberId.FiberId + ): Effect + (register: (callback: (_: Effect) => void) => void, blockingOn?: FiberId.FiberId): Effect + ( + register: (callback: (_: Effect) => void, signal: AbortSignal) => void, + blockingOn?: FiberId.FiberId + ): Effect +} = core.async /** * Converts an asynchronous, callback-style API into an `Effect`, which will @@ -915,24 +922,6 @@ export const asyncEffect: ( register: (callback: (_: Effect) => void) => Effect ) => Effect = _runtime.asyncEffect -/** - * Imports an asynchronous side-effect into a pure `Effect` value. - * The callback function `Effect => void` must be called at most once. - * - * The registration function receives an AbortSignal that can be used to handle - * interruption. - * - * The `FiberId` of the fiber that may complete the async callback may be - * provided to allow for better diagnostics. - * - * @since 1.0.0 - * @category constructors - */ -export const asyncInterrupt: ( - register: (callback: (_: Effect) => void, signal: AbortSignal) => void, - blockingOn?: FiberId.FiberId -) => Effect = core.asyncInterrupt - /** * Imports an asynchronous effect into a pure `Effect` value, possibly returning * the value synchronously. @@ -1335,16 +1324,10 @@ export const none: (self: Effect>) => Effect(evaluate: LazyArg>) => Effect = effect.promise - -/** - * Like `promise` but allows for interruption via AbortSignal - * - * @since 1.0.0 - * @category constructors - */ -export const promiseInterrupt: (evaluate: (signal: AbortSignal) => Promise) => Effect = - effect.promiseInterrupt +export const promise: { + (evaluate: (signal: AbortSignal) => Promise): Effect + (evaluate: LazyArg>): Effect +} = effect.promise /** * @since 1.0.0 @@ -1785,40 +1768,24 @@ export const tryMap: { export const tryMapPromise: { ( options: { - readonly try: (a: A) => Promise + readonly try: (a: A, signal: AbortSignal) => Promise readonly catch: (error: unknown) => E1 - } - ): (self: Effect) => Effect - ( - self: Effect, - options: { + } | { readonly try: (a: A) => Promise readonly catch: (error: unknown) => E1 } - ): Effect -} = effect.tryMapPromise - -/** - * Like `tryMapPromise` but allows for interruption via AbortSignal - * - * @since 1.0.0 - * @category error handling - */ -export const tryMapPromiseInterrupt: { - ( - options: { - readonly try: (a: A, signal: AbortSignal) => Promise - readonly catch: (error: unknown) => E1 - } ): (self: Effect) => Effect ( self: Effect, options: { readonly try: (a: A, signal: AbortSignal) => Promise readonly catch: (error: unknown) => E1 + } | { + readonly try: (a: A) => Promise + readonly catch: (error: unknown) => E1 } ): Effect -} = effect.tryMapPromiseInterrupt +} = effect.tryMapPromise /** * Create an `Effect` that when executed will construct `promise` and wait for @@ -1828,22 +1795,17 @@ export const tryMapPromiseInterrupt: { * @category error handling */ export const tryPromise: { - (options: { readonly try: LazyArg>; readonly catch: (error: unknown) => E }): Effect - (try_: LazyArg>): Effect -} = effect.tryPromise - -/** - * Like `tryPromise` but allows for interruption via AbortSignal - * - * @since 1.0.0 - * @category error handling - */ -export const tryPromiseInterrupt: { ( - options: { readonly try: (signal: AbortSignal) => Promise; readonly catch: (error: unknown) => E } + options: { + readonly try: LazyArg> + readonly catch: (error: unknown) => E + } | { + readonly try: (signal: AbortSignal) => Promise + readonly catch: (error: unknown) => E + } ): Effect - (try_: (signal: AbortSignal) => Promise): Effect -} = effect.tryPromiseInterrupt + (try_: LazyArg> | ((signal: AbortSignal) => Promise)): Effect +} = effect.tryPromise /** * The inverse operation `sandbox(effect)` diff --git a/src/internal/core.ts b/src/internal/core.ts index f19405f7..be44be86 100644 --- a/src/internal/core.ts +++ b/src/internal/core.ts @@ -392,19 +392,49 @@ export const as = dual< export const asUnit = (self: Effect.Effect): Effect.Effect => as(self, void 0) /* @internal */ -export const async = ( - register: (callback: (_: Effect.Effect) => void) => void | Effect.Effect, +export const async: { + ( + register: (callback: (_: Effect.Effect) => void) => Effect.Effect, + blockingOn?: FiberId.FiberId + ): Effect.Effect + ( + register: (callback: (_: Effect.Effect) => void) => void, + blockingOn?: FiberId.FiberId + ): Effect.Effect + ( + register: (callback: (_: Effect.Effect) => void, signal: AbortSignal) => void, + blockingOn?: FiberId.FiberId + ): Effect.Effect +} = ( + register: ( + callback: (_: Effect.Effect) => void, + signal: AbortSignal + ) => void | Effect.Effect, blockingOn: FiberId.FiberId = FiberId.none -): Effect.Effect => - suspend(() => { +): Effect.Effect => { + if (register.length >= 2) { + return suspend(() => { + const controller = new AbortController() + const effect = new EffectPrimitive(OpCodes.OP_ASYNC) as any + effect.i0 = (resume: (_: Effect.Effect) => void) => { + register(resume, controller.signal) + } + effect.i1 = blockingOn + return onInterrupt(effect, () => sync(() => controller.abort())) + }) + } + + return suspend(() => { let cancelerRef: Effect.Effect | void = undefined const effect = new EffectPrimitive(OpCodes.OP_ASYNC) as any effect.i0 = (resume: (_: Effect.Effect) => void) => { - cancelerRef = register(resume) + cancelerRef = + (register as (callback: (_: Effect.Effect) => void) => void | Effect.Effect)(resume) } effect.i1 = blockingOn return onInterrupt(effect, () => isEffect(cancelerRef) ? cancelerRef : unit) }) +} /* @internal */ export const asyncEither = ( @@ -422,19 +452,6 @@ export const asyncEither = ( } }, blockingOn) -/** @internal */ -export const asyncInterrupt = ( - register: (callback: (_: Effect.Effect) => void, signal: AbortSignal) => void, - blockingOn: FiberId.FiberId = FiberId.none -): Effect.Effect => - async((resume) => { - const controller = new AbortController() - register(resume, controller.signal) - return sync(() => { - controller.abort() - }) - }, blockingOn) - /* @internal */ export const catchAllCause = dual< ( diff --git a/src/internal/effect.ts b/src/internal/effect.ts index 5c8a72b4..12f19374 100644 --- a/src/internal/effect.ts +++ b/src/internal/effect.ts @@ -1158,22 +1158,21 @@ export const patchFiberRefs = (patch: FiberRefsPatch.FiberRefsPatch): Effect.Eff updateFiberRefs((fiberId, fiberRefs) => pipe(patch, fiberRefsPatch.patch(fiberId, fiberRefs))) /* @internal */ -export const promise = (evaluate: LazyArg>): Effect.Effect => - core.async((resolve) => { - evaluate() - .then((a) => resolve(core.exitSucceed(a))) - .catch((e) => resolve(core.exitDie(e))) - }) - -/* @internal */ -export const promiseInterrupt = (evaluate: (signal: AbortSignal) => Promise): Effect.Effect => - core.asyncEither((resolve) => { - const controller = new AbortController() - evaluate(controller.signal) - .then((a) => resolve(core.exitSucceed(a))) - .catch((e) => resolve(core.exitDie(e))) - return Either.left(core.sync(() => controller.abort())) - }) +export const promise: { + (evaluate: (signal: AbortSignal) => Promise): Effect.Effect + (evaluate: LazyArg>): Effect.Effect +} = (evaluate: (signal: AbortSignal) => Promise): Effect.Effect => + evaluate.length >= 1 ? + core.async((resolve, signal) => { + evaluate(signal) + .then((a) => resolve(core.exitSucceed(a))) + .catch((e) => resolve(core.exitDie(e))) + }) : + core.async((resolve) => { + ;(evaluate as LazyArg>)() + .then((a) => resolve(core.exitSucceed(a))) + .catch((e) => resolve(core.exitDie(e))) + }) /* @internal */ export const provideService = dual< @@ -1582,56 +1581,55 @@ export const tryPromise: { options: { readonly try: LazyArg> readonly catch: (error: unknown) => E + } | { + readonly try: (signal: AbortSignal) => Promise + readonly catch: (error: unknown) => E } ): Effect.Effect - (try_: LazyArg>): Effect.Effect + (try_: LazyArg> | ((signal: AbortSignal) => Promise)): Effect.Effect } = ( - arg: LazyArg> | { readonly try: LazyArg>; readonly catch: (error: unknown) => E } -): Effect.Effect => - core.flatMap(try_(arg as LazyArg>), (promise) => + arg: (LazyArg> | ((signal: AbortSignal) => Promise)) | { + readonly try: LazyArg> | ((signal: AbortSignal) => Promise) + readonly catch: (error: unknown) => E + } +): Effect.Effect => { + let evaluate: (signal?: AbortSignal) => Promise + let catcher: ((error: unknown) => E) | undefined = undefined + + if (typeof arg === "function") { + evaluate = arg as any + } else { + evaluate = arg.try as any + catcher = arg.catch + } + + if (evaluate.length >= 1) { + return core.suspend(() => { + const controller = new AbortController() + return core.flatMap(try_(() => evaluate(controller.signal)), (promise) => + core.async((resolve) => { + promise + .then((a) => resolve(core.exitSucceed(a))) + .catch((e) => + resolve(core.exitFail( + catcher ? catcher(e) : e + )) + ) + return core.sync(() => controller.abort()) + })) + }) + } + + return core.flatMap(try_(arg as LazyArg>), (promise) => core.async((resolve) => { promise .then((a) => resolve(core.exitSucceed(a))) .catch((e) => resolve(core.exitFail( - "catch" in arg ? arg.catch(e) : e + catcher ? catcher(e) : e )) ) })) - -/* @internal */ -export const tryPromiseInterrupt: { - ( - options: { - readonly try: (signal: AbortSignal) => Promise - readonly catch: (error: unknown) => E - } - ): Effect.Effect - (try_: (signal: AbortSignal) => Promise): Effect.Effect -} = ( - arg: - | ((signal: AbortSignal) => Promise) - | { - readonly try: (signal: AbortSignal) => Promise - readonly catch: (error: unknown) => E - } -): Effect.Effect => { - const hasCatch = "catch" in arg - const evaluate = () => { - const controller = new AbortController() - return [controller, hasCatch ? arg.try(controller.signal) : arg(controller.signal)] as const - } - - return core.flatMap( - hasCatch ? try_({ try: evaluate, catch: arg.catch }) : try_(evaluate), - ([controller, promise]) => - core.asyncEither((resolve) => { - promise - .then((a) => resolve(core.exitSucceed(a))) - .catch((e) => resolve(core.exitFail(hasCatch ? arg.catch(e) : e))) - return Either.left(core.sync(() => controller.abort())) - }) - ) } /* @internal */ @@ -1660,43 +1658,29 @@ export const tryMap = dual< export const tryMapPromise = dual< ( options: { - readonly try: (a: A) => Promise + readonly try: (a: A, signal: AbortSignal) => Promise readonly catch: (error: unknown) => E1 - } - ) => (self: Effect.Effect) => Effect.Effect, - ( - self: Effect.Effect, - options: { + } | { readonly try: (a: A) => Promise readonly catch: (error: unknown) => E1 } - ) => Effect.Effect ->(2, (self, options) => - core.flatMap(self, (a) => - tryPromise({ - try: () => options.try(a), - catch: options.catch - }))) - -/* @internal */ -export const tryMapPromiseInterrupt = dual< - ( - options: { - readonly try: (a: A, signal: AbortSignal) => Promise - readonly catch: (error: unknown) => E1 - } ) => (self: Effect.Effect) => Effect.Effect, ( self: Effect.Effect, options: { readonly try: (a: A, signal: AbortSignal) => Promise readonly catch: (error: unknown) => E1 + } | { + readonly try: (a: A) => Promise + readonly catch: (error: unknown) => E1 } ) => Effect.Effect >(2, (self, options) => core.flatMap(self, (a) => - tryPromiseInterrupt({ - try: (signal) => options.try(a, signal), + tryPromise({ + try: options.try.length >= 1 ? + (signal) => options.try(a, signal) : + () => (options.try as any)(a), catch: options.catch }))) diff --git a/src/internal/fiberRuntime.ts b/src/internal/fiberRuntime.ts index d75a97d5..785d4dd4 100644 --- a/src/internal/fiberRuntime.ts +++ b/src/internal/fiberRuntime.ts @@ -379,7 +379,7 @@ export class FiberRuntime implements Fiber.RuntimeFiber { } await(): Effect.Effect> { - return core.asyncInterrupt>((resume) => { + return core.async>((resume) => { const cb = (exit: Exit.Exit) => resume(core.succeed(exit)) this.tell( FiberMessage.stateful((fiber, _) => { @@ -3246,7 +3246,7 @@ export const invokeWithInterrupt: ( core.flatMap( forkDaemon(core.interruptible(dataSource)), (processing) => - core.asyncInterrupt((cb) => { + core.async((cb) => { const counts = all.map((_) => _.listeners.count) const checkDone = () => { if (counts.every((count) => count === 0)) { diff --git a/test/Effect/interruption.ts b/test/Effect/interruption.ts index e122df90..5956a22d 100644 --- a/test/Effect/interruption.ts +++ b/test/Effect/interruption.ts @@ -588,11 +588,11 @@ describe.concurrent("Effect", () => { const result = yield* $(Deferred.await(deferred)) assert.strictEqual(result, 42) })) - it.effect("asyncInterrupt aborts the signal", () => + it.effect("AbortSignal is aborted", () => Effect.gen(function*($) { let signal: AbortSignal const fiber = yield* $( - Effect.asyncInterrupt((cb, signal_) => { + Effect.async((_cb, signal_) => { signal = signal_ }), Effect.fork From 8283985c9914bea2e57fb08a3d8b6d42d747932a Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 29 Jul 2023 13:11:21 +1200 Subject: [PATCH 2/5] docs --- docs/modules/Effect.ts.md | 118 ++++++++++---------------------------- 1 file changed, 29 insertions(+), 89 deletions(-) diff --git a/docs/modules/Effect.ts.md b/docs/modules/Effect.ts.md index 685214af..3caebc27 100644 --- a/docs/modules/Effect.ts.md +++ b/docs/modules/Effect.ts.md @@ -64,7 +64,6 @@ Added in v1.0.0 - [async](#async) - [asyncEffect](#asynceffect) - [asyncEither](#asynceither) - - [asyncInterrupt](#asyncinterrupt) - [asyncOption](#asyncoption) - [die](#die) - [dieMessage](#diemessage) @@ -77,7 +76,6 @@ Added in v1.0.0 - [never](#never) - [none](#none) - [promise](#promise) - - [promiseInterrupt](#promiseinterrupt) - [succeed](#succeed) - [succeedNone](#succeednone) - [succeedSome](#succeedsome) @@ -150,9 +148,7 @@ Added in v1.0.0 - [try](#try) - [tryMap](#trymap) - [tryMapPromise](#trymappromise) - - [tryMapPromiseInterrupt](#trymappromiseinterrupt) - [tryPromise](#trypromise) - - [tryPromiseInterrupt](#trypromiseinterrupt) - [unsandbox](#unsandbox) - [execution](#execution) - [runCallback](#runcallback) @@ -1160,10 +1156,17 @@ provided to allow for better diagnostics. **Signature** ```ts -export declare const async: ( - register: (callback: (_: Effect) => void) => void | Effect, - blockingOn?: FiberId.FiberId -) => Effect +export declare const async: { + ( + register: (callback: (_: Effect) => void) => Effect, + blockingOn?: FiberId.FiberId + ): Effect + (register: (callback: (_: Effect) => void) => void, blockingOn?: FiberId.FiberId): Effect + ( + register: (callback: (_: Effect) => void, signal: AbortSignal) => void, + blockingOn?: FiberId.FiberId + ): Effect +} ``` Added in v1.0.0 @@ -1212,28 +1215,6 @@ export declare const asyncEither: ( Added in v1.0.0 -## asyncInterrupt - -Imports an asynchronous side-effect into a pure `Effect` value. -The callback function `Effect => void` must be called at most once. - -The registration function receives an AbortSignal that can be used to handle -interruption. - -The `FiberId` of the fiber that may complete the async callback may be -provided to allow for better diagnostics. - -**Signature** - -```ts -export declare const asyncInterrupt: ( - register: (callback: (_: Effect) => void, signal: AbortSignal) => void, - blockingOn?: FiberId.FiberId -) => Effect -``` - -Added in v1.0.0 - ## asyncOption Imports an asynchronous effect into a pure `Effect` value, possibly returning @@ -1379,19 +1360,10 @@ Like `tryPromise` but produces a defect in case of errors. **Signature** ```ts -export declare const promise: (evaluate: LazyArg>) => Effect -``` - -Added in v1.0.0 - -## promiseInterrupt - -Like `promise` but allows for interruption via AbortSignal - -**Signature** - -```ts -export declare const promiseInterrupt: (evaluate: (signal: AbortSignal) => Promise) => Effect +export declare const promise: { + (evaluate: (signal: AbortSignal) => Promise): Effect + (evaluate: LazyArg>): Effect +} ``` Added in v1.0.0 @@ -2511,33 +2483,16 @@ Returns an effect whose success is mapped by the specified side effecting ```ts export declare const tryMapPromise: { - (options: { readonly try: (a: A) => Promise; readonly catch: (error: unknown) => E1 }): ( - self: Effect - ) => Effect + ( + options: + | { readonly try: (a: A, signal: AbortSignal) => Promise; readonly catch: (error: unknown) => E1 } + | { readonly try: (a: A) => Promise; readonly catch: (error: unknown) => E1 } + ): (self: Effect) => Effect ( self: Effect, - options: { readonly try: (a: A) => Promise; readonly catch: (error: unknown) => E1 } - ): Effect -} -``` - -Added in v1.0.0 - -## tryMapPromiseInterrupt - -Like `tryMapPromise` but allows for interruption via AbortSignal - -**Signature** - -```ts -export declare const tryMapPromiseInterrupt: { - (options: { - readonly try: (a: A, signal: AbortSignal) => Promise - readonly catch: (error: unknown) => E1 - }): (self: Effect) => Effect - ( - self: Effect, - options: { readonly try: (a: A, signal: AbortSignal) => Promise; readonly catch: (error: unknown) => E1 } + options: + | { readonly try: (a: A, signal: AbortSignal) => Promise; readonly catch: (error: unknown) => E1 } + | { readonly try: (a: A) => Promise; readonly catch: (error: unknown) => E1 } ): Effect } ``` @@ -2553,27 +2508,12 @@ its result, errors will produce failure as `unknown`. ```ts export declare const tryPromise: { - (options: { readonly try: LazyArg>; readonly catch: (error: unknown) => E }): Effect - (try_: LazyArg>): Effect -} -``` - -Added in v1.0.0 - -## tryPromiseInterrupt - -Like `tryPromise` but allows for interruption via AbortSignal - -**Signature** - -```ts -export declare const tryPromiseInterrupt: { - (options: { readonly try: (signal: AbortSignal) => Promise; readonly catch: (error: unknown) => E }): Effect< - never, - E, - A - > - (try_: (signal: AbortSignal) => Promise): Effect + ( + options: + | { readonly try: LazyArg>; readonly catch: (error: unknown) => E } + | { readonly try: (signal: AbortSignal) => Promise; readonly catch: (error: unknown) => E } + ): Effect + (try_: LazyArg> | ((signal: AbortSignal) => Promise)): Effect } ``` From 4c22c7037b57cd49c8e2e72821c1e701e9177ff2 Mon Sep 17 00:00:00 2001 From: Michael Arnaldi Date: Fri, 28 Jul 2023 14:44:42 +0000 Subject: [PATCH 3/5] unify variants --- src/Effect.ts | 15 ++++---------- src/internal/core.ts | 47 +++++++++++++++++++++----------------------- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/Effect.ts b/src/Effect.ts index 99bd78fb..5afd7b2f 100644 --- a/src/Effect.ts +++ b/src/Effect.ts @@ -897,17 +897,10 @@ export const validateFirst: { * @since 1.0.0 * @category constructors */ -export const async: { - ( - register: (callback: (_: Effect) => void) => Effect, - blockingOn?: FiberId.FiberId - ): Effect - (register: (callback: (_: Effect) => void) => void, blockingOn?: FiberId.FiberId): Effect - ( - register: (callback: (_: Effect) => void, signal: AbortSignal) => void, - blockingOn?: FiberId.FiberId - ): Effect -} = core.async +export const async: ( + register: (callback: (_: Effect) => void, signal: AbortSignal) => void | Effect, + blockingOn?: FiberId.FiberId +) => Effect = core.async /** * Converts an asynchronous, callback-style API into an `Effect`, which will diff --git a/src/internal/core.ts b/src/internal/core.ts index be44be86..56f33486 100644 --- a/src/internal/core.ts +++ b/src/internal/core.ts @@ -394,15 +394,10 @@ export const asUnit = (self: Effect.Effect): Effect.Effect( - register: (callback: (_: Effect.Effect) => void) => Effect.Effect, - blockingOn?: FiberId.FiberId - ): Effect.Effect - ( - register: (callback: (_: Effect.Effect) => void) => void, - blockingOn?: FiberId.FiberId - ): Effect.Effect - ( - register: (callback: (_: Effect.Effect) => void, signal: AbortSignal) => void, + register: ( + callback: (_: Effect.Effect) => void, + signal: AbortSignal + ) => Effect.Effect | void, blockingOn?: FiberId.FiberId ): Effect.Effect } = ( @@ -412,27 +407,29 @@ export const async: { ) => void | Effect.Effect, blockingOn: FiberId.FiberId = FiberId.none ): Effect.Effect => { - if (register.length >= 2) { - return suspend(() => { - const controller = new AbortController() - const effect = new EffectPrimitive(OpCodes.OP_ASYNC) as any - effect.i0 = (resume: (_: Effect.Effect) => void) => { - register(resume, controller.signal) - } - effect.i1 = blockingOn - return onInterrupt(effect, () => sync(() => controller.abort())) - }) - } - return suspend(() => { let cancelerRef: Effect.Effect | void = undefined + let controllerRef: AbortController | void = undefined const effect = new EffectPrimitive(OpCodes.OP_ASYNC) as any - effect.i0 = (resume: (_: Effect.Effect) => void) => { - cancelerRef = - (register as (callback: (_: Effect.Effect) => void) => void | Effect.Effect)(resume) + if (register.length !== 1) { + const controller = new AbortController() + controllerRef = controller + effect.i0 = (resume: (_: Effect.Effect) => void) => { + cancelerRef = register(resume, controller.signal) + } + } else { + effect.i0 = (resume: (_: Effect.Effect) => void) => { + // @ts-expect-error + cancelerRef = register(resume) + } } effect.i1 = blockingOn - return onInterrupt(effect, () => isEffect(cancelerRef) ? cancelerRef : unit) + return onInterrupt(effect, () => { + if (controllerRef) { + controllerRef.abort() + } + return cancelerRef ?? unit + }) }) } From 94029281aeaa15dd0af459ddeb71628ea6012ea4 Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 29 Jul 2023 13:23:27 +1200 Subject: [PATCH 4/5] type cleanup --- docs/modules/Effect.ts.md | 31 +++++++------------- src/Effect.ts | 12 +++----- src/internal/core.ts | 15 ++-------- src/internal/effect.ts | 60 +++++++++++++++++++++------------------ 4 files changed, 50 insertions(+), 68 deletions(-) diff --git a/docs/modules/Effect.ts.md b/docs/modules/Effect.ts.md index 3caebc27..92e99d90 100644 --- a/docs/modules/Effect.ts.md +++ b/docs/modules/Effect.ts.md @@ -1156,17 +1156,10 @@ provided to allow for better diagnostics. **Signature** ```ts -export declare const async: { - ( - register: (callback: (_: Effect) => void) => Effect, - blockingOn?: FiberId.FiberId - ): Effect - (register: (callback: (_: Effect) => void) => void, blockingOn?: FiberId.FiberId): Effect - ( - register: (callback: (_: Effect) => void, signal: AbortSignal) => void, - blockingOn?: FiberId.FiberId - ): Effect -} +export declare const async: ( + register: (callback: (_: Effect) => void, signal: AbortSignal) => void | Effect, + blockingOn?: FiberId.FiberId +) => Effect ``` Added in v1.0.0 @@ -1360,10 +1353,9 @@ Like `tryPromise` but produces a defect in case of errors. **Signature** ```ts -export declare const promise: { - (evaluate: (signal: AbortSignal) => Promise): Effect - (evaluate: LazyArg>): Effect -} +export declare const promise: ( + evaluate: LazyArg> | ((signal: AbortSignal) => Promise) +) => Effect ``` Added in v1.0.0 @@ -2508,11 +2500,10 @@ its result, errors will produce failure as `unknown`. ```ts export declare const tryPromise: { - ( - options: - | { readonly try: LazyArg>; readonly catch: (error: unknown) => E } - | { readonly try: (signal: AbortSignal) => Promise; readonly catch: (error: unknown) => E } - ): Effect + (options: { + readonly try: LazyArg> | ((signal: AbortSignal) => Promise) + readonly catch: (error: unknown) => E + }): Effect (try_: LazyArg> | ((signal: AbortSignal) => Promise)): Effect } ``` diff --git a/src/Effect.ts b/src/Effect.ts index 5afd7b2f..e276c4c5 100644 --- a/src/Effect.ts +++ b/src/Effect.ts @@ -1317,10 +1317,9 @@ export const none: (self: Effect>) => Effect(evaluate: (signal: AbortSignal) => Promise): Effect - (evaluate: LazyArg>): Effect -} = effect.promise +export const promise: ( + evaluate: LazyArg> | ((signal: AbortSignal) => Promise) +) => Effect = effect.promise /** * @since 1.0.0 @@ -1790,10 +1789,7 @@ export const tryMapPromise: { export const tryPromise: { ( options: { - readonly try: LazyArg> - readonly catch: (error: unknown) => E - } | { - readonly try: (signal: AbortSignal) => Promise + readonly try: LazyArg> | ((signal: AbortSignal) => Promise) readonly catch: (error: unknown) => E } ): Effect diff --git a/src/internal/core.ts b/src/internal/core.ts index 56f33486..088971ff 100644 --- a/src/internal/core.ts +++ b/src/internal/core.ts @@ -392,22 +392,14 @@ export const as = dual< export const asUnit = (self: Effect.Effect): Effect.Effect => as(self, void 0) /* @internal */ -export const async: { - ( - register: ( - callback: (_: Effect.Effect) => void, - signal: AbortSignal - ) => Effect.Effect | void, - blockingOn?: FiberId.FiberId - ): Effect.Effect -} = ( +export const async = ( register: ( callback: (_: Effect.Effect) => void, signal: AbortSignal ) => void | Effect.Effect, blockingOn: FiberId.FiberId = FiberId.none -): Effect.Effect => { - return suspend(() => { +): Effect.Effect => + suspend(() => { let cancelerRef: Effect.Effect | void = undefined let controllerRef: AbortController | void = undefined const effect = new EffectPrimitive(OpCodes.OP_ASYNC) as any @@ -431,7 +423,6 @@ export const async: { return cancelerRef ?? unit }) }) -} /* @internal */ export const asyncEither = ( diff --git a/src/internal/effect.ts b/src/internal/effect.ts index 12f19374..52ff9d29 100644 --- a/src/internal/effect.ts +++ b/src/internal/effect.ts @@ -1159,8 +1159,7 @@ export const patchFiberRefs = (patch: FiberRefsPatch.FiberRefsPatch): Effect.Eff /* @internal */ export const promise: { - (evaluate: (signal: AbortSignal) => Promise): Effect.Effect - (evaluate: LazyArg>): Effect.Effect + (evaluate: LazyArg> | ((signal: AbortSignal) => Promise)): Effect.Effect } = (evaluate: (signal: AbortSignal) => Promise): Effect.Effect => evaluate.length >= 1 ? core.async((resolve, signal) => { @@ -1579,10 +1578,7 @@ export const tracer: Effect.Effect = tracerWith(cor export const tryPromise: { ( options: { - readonly try: LazyArg> - readonly catch: (error: unknown) => E - } | { - readonly try: (signal: AbortSignal) => Promise + readonly try: LazyArg> | ((signal: AbortSignal) => Promise) readonly catch: (error: unknown) => E } ): Effect.Effect @@ -1597,9 +1593,9 @@ export const tryPromise: { let catcher: ((error: unknown) => E) | undefined = undefined if (typeof arg === "function") { - evaluate = arg as any + evaluate = arg as (signal?: AbortSignal) => Promise } else { - evaluate = arg.try as any + evaluate = arg.try as (signal?: AbortSignal) => Promise catcher = arg.catch } @@ -1620,16 +1616,24 @@ export const tryPromise: { }) } - return core.flatMap(try_(arg as LazyArg>), (promise) => - core.async((resolve) => { - promise - .then((a) => resolve(core.exitSucceed(a))) - .catch((e) => - resolve(core.exitFail( - catcher ? catcher(e) : e - )) - ) - })) + return core.flatMap( + try_( + arg as { + readonly try: LazyArg> + readonly catch: (error: unknown) => E + } + ), + (promise) => + core.async((resolve) => { + promise + .then((a) => resolve(core.exitSucceed(a))) + .catch((e) => + resolve(core.exitFail( + catcher ? catcher(e) : e + )) + ) + }) + ) } /* @internal */ @@ -1658,29 +1662,29 @@ export const tryMap = dual< export const tryMapPromise = dual< ( options: { - readonly try: (a: A, signal: AbortSignal) => Promise - readonly catch: (error: unknown) => E1 - } | { - readonly try: (a: A) => Promise + readonly try: ((a: A) => Promise) | ((a: A, signal: AbortSignal) => Promise) readonly catch: (error: unknown) => E1 } ) => (self: Effect.Effect) => Effect.Effect, ( self: Effect.Effect, options: { - readonly try: (a: A, signal: AbortSignal) => Promise - readonly catch: (error: unknown) => E1 - } | { - readonly try: (a: A) => Promise + readonly try: ((a: A) => Promise) | ((a: A, signal: AbortSignal) => Promise) readonly catch: (error: unknown) => E1 } ) => Effect.Effect ->(2, (self, options) => +>(2, ( + self: Effect.Effect, + options: { + readonly try: ((a: A) => Promise) | ((a: A, signal: AbortSignal) => Promise) + readonly catch: (error: unknown) => E1 + } +): Effect.Effect => core.flatMap(self, (a) => tryPromise({ try: options.try.length >= 1 ? (signal) => options.try(a, signal) : - () => (options.try as any)(a), + () => (options.try as (a: A) => Promise)(a), catch: options.catch }))) From a8af94f892d32c9da8c77d5d1e362ddbca96d55e Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 29 Jul 2023 13:38:45 +1200 Subject: [PATCH 5/5] add more docs --- docs/modules/Effect.ts.md | 12 ++++++++++++ src/Effect.ts | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/docs/modules/Effect.ts.md b/docs/modules/Effect.ts.md index 92e99d90..c1a8b2d1 100644 --- a/docs/modules/Effect.ts.md +++ b/docs/modules/Effect.ts.md @@ -1150,6 +1150,9 @@ The callback function `Effect => void` must be called at most once. If an Effect is returned by the registration function, it will be executed if the fiber executing the effect is interrupted. +The registration function can also receive an `AbortSignal` if required for +interruption. + The `FiberId` of the fiber that may complete the async callback may be provided to allow for better diagnostics. @@ -1350,6 +1353,9 @@ Added in v1.0.0 Like `tryPromise` but produces a defect in case of errors. +An optional `AbortSignal` can be provided to allow for interruption of the +wrapped Promise api. + **Signature** ```ts @@ -2471,6 +2477,9 @@ Added in v1.0.0 Returns an effect whose success is mapped by the specified side effecting `f` function, translating any promise rejections into typed failed effects. +An optional `AbortSignal` can be provided to allow for interruption of the +wrapped Promise api. + **Signature** ```ts @@ -2496,6 +2505,9 @@ Added in v1.0.0 Create an `Effect` that when executed will construct `promise` and wait for its result, errors will produce failure as `unknown`. +An optional `AbortSignal` can be provided to allow for interruption of the +wrapped Promise api. + **Signature** ```ts diff --git a/src/Effect.ts b/src/Effect.ts index e276c4c5..a4ba08da 100644 --- a/src/Effect.ts +++ b/src/Effect.ts @@ -891,6 +891,9 @@ export const validateFirst: { * If an Effect is returned by the registration function, it will be executed * if the fiber executing the effect is interrupted. * + * The registration function can also receive an `AbortSignal` if required for + * interruption. + * * The `FiberId` of the fiber that may complete the async callback may be * provided to allow for better diagnostics. * @@ -1314,6 +1317,9 @@ export const none: (self: Effect>) => Effect