Skip to content

Commit

Permalink
Make runtime run sync dual (#4374)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim <[email protected]>
  • Loading branch information
2 people authored and effect-bot committed Mar 3, 2025
1 parent 3676392 commit 96d29f5
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .changeset/yellow-lemons-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

Make Runtime.run* apis dual
74 changes: 54 additions & 20 deletions packages/effect/src/Runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,16 @@ export interface RunForkOptions {
* @since 2.0.0
* @category execution
*/
export const runFork: <R>(
runtime: Runtime<R>
) => <A, E>(self: Effect.Effect<A, E, R>, options?: RunForkOptions) => Fiber.RuntimeFiber<A, E> = internal.unsafeFork
export const runFork: {
<R>(
runtime: Runtime<R>
): <A, E>(effect: Effect.Effect<A, E, R>, options?: RunForkOptions | undefined) => Fiber.RuntimeFiber<A, E>
<R, A, E>(
runtime: Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: RunForkOptions | undefined
): Fiber.RuntimeFiber<A, E>
} = internal.unsafeFork

/**
* Executes the effect synchronously returning the exit.
Expand All @@ -94,8 +101,10 @@ export const runFork: <R>(
* @since 2.0.0
* @category execution
*/
export const runSyncExit: <R>(runtime: Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>) => Exit.Exit<A, E> =
internal.unsafeRunSyncExit
export const runSyncExit: {
<A, E, R>(runtime: Runtime<R>, effect: Effect.Effect<A, E, R>): Exit.Exit<A, E>
<R>(runtime: Runtime<R>): <A, E>(effect: Effect.Effect<A, E, R>) => Exit.Exit<A, E>
} = internal.unsafeRunSyncExit

/**
* Executes the effect synchronously throwing in case of errors or async boundaries.
Expand All @@ -106,7 +115,10 @@ export const runSyncExit: <R>(runtime: Runtime<R>) => <A, E>(effect: Effect.Effe
* @since 2.0.0
* @category execution
*/
export const runSync: <R>(runtime: Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>) => A = internal.unsafeRunSync
export const runSync: {
<A, E, R>(runtime: Runtime<R>, effect: Effect.Effect<A, E, R>): A
<R>(runtime: Runtime<R>): <A, E>(effect: Effect.Effect<A, E, R>) => A
} = internal.unsafeRunSync

/**
* @since 2.0.0
Expand All @@ -126,10 +138,19 @@ export interface RunCallbackOptions<in A, in E = never> extends RunForkOptions {
* @since 2.0.0
* @category execution
*/
export const runCallback: <R>(
runtime: Runtime<R>
) => <A, E>(effect: Effect.Effect<A, E, R>, options?: RunCallbackOptions<A, E> | undefined) => Cancel<A, E> =
internal.unsafeRunCallback
export const runCallback: {
<R>(
runtime: Runtime<R>
): <A, E>(
effect: Effect.Effect<A, E, R>,
options?: RunCallbackOptions<A, E> | undefined
) => (fiberId?: FiberId.FiberId, options?: RunCallbackOptions<A, E> | undefined) => void
<R, A, E>(
runtime: Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: RunCallbackOptions<A, E> | undefined
): (fiberId?: FiberId.FiberId, options?: RunCallbackOptions<A, E> | undefined) => void
} = internal.unsafeRunCallback

/**
* Runs the `Effect`, returning a JavaScript `Promise` that will be resolved
Expand All @@ -142,10 +163,16 @@ export const runCallback: <R>(
* @since 2.0.0
* @category execution
*/
export const runPromise: <R>(
runtime: Runtime<R>
) => <A, E>(effect: Effect.Effect<A, E, R>, options?: { readonly signal?: AbortSignal } | undefined) => Promise<A> =
internal.unsafeRunPromise
export const runPromise: {
<R>(
runtime: Runtime<R>
): <A, E>(effect: Effect.Effect<A, E, R>, options?: { readonly signal?: AbortSignal } | undefined) => Promise<A>
<R, A, E>(
runtime: Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
): Promise<A>
} = internal.unsafeRunPromise

/**
* Runs the `Effect`, returning a JavaScript `Promise` that will be resolved
Expand All @@ -157,12 +184,19 @@ export const runPromise: <R>(
* @since 2.0.0
* @category execution
*/
export const runPromiseExit: <R>(
runtime: Runtime<R>
) => <A, E>(
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
) => Promise<Exit.Exit<A, E>> = internal.unsafeRunPromiseExit
export const runPromiseExit: {
<R>(
runtime: Runtime<R>
): <A, E>(
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
) => Promise<Exit.Exit<A, E>>
<R, A, E>(
runtime: Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
): Promise<Exit.Exit<A, E>>
} = internal.unsafeRunPromiseExit

/**
* @since 2.0.0
Expand Down
142 changes: 104 additions & 38 deletions packages/effect/src/internal/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,33 @@ import * as OpCodes from "./opCodes/effect.js"
import * as runtimeFlags from "./runtimeFlags.js"
import * as supervisor_ from "./supervisor.js"

const makeDual = <Args extends Array<any>, Return>(
f: (runtime: Runtime.Runtime<never>, effect: Effect.Effect<any, any>, ...args: Args) => Return
): {
<R>(runtime: Runtime.Runtime<R>): <A, E>(effect: Effect.Effect<A, E, R>, ...args: Args) => Return
<R, A, E>(runtime: Runtime.Runtime<R>, effect: Effect.Effect<A, E, R>, ...args: Args): Return
} =>
function(this: any) {
if (arguments.length === 1) {
const runtime = arguments[0]
return (effect: any, ...args: Args) => f(runtime, effect, ...args)
}
return f.apply(this, arguments as any)
} as any

/** @internal */
export const unsafeFork = <R>(runtime: Runtime.Runtime<R>) =>
<A, E>(
export const unsafeFork: {
<R>(runtime: Runtime.Runtime<R>): <A, E>(
effect: Effect.Effect<A, E, R>,
options?: Runtime.RunForkOptions | undefined
) => Fiber.RuntimeFiber<A, E>
<R, A, E>(
runtime: Runtime.Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: Runtime.RunForkOptions | undefined
): Fiber.RuntimeFiber<A, E>
} = makeDual(<R, A, E>(
runtime: Runtime.Runtime<R>,
self: Effect.Effect<A, E, R>,
options?: Runtime.RunForkOptions
): Fiber.RuntimeFiber<A, E> => {
Expand Down Expand Up @@ -93,15 +117,25 @@ export const unsafeFork = <R>(runtime: Runtime.Runtime<R>) =>
}

return fiberRuntime
}
})

/** @internal */
export const unsafeRunCallback = <R>(runtime: Runtime.Runtime<R>) =>
<A, E>(
effect: Effect.Effect<A, E, R>,
options: Runtime.RunCallbackOptions<A, E> = {}
): (fiberId?: FiberId.FiberId, options?: Runtime.RunCallbackOptions<A, E> | undefined) => void => {
const fiberRuntime = unsafeFork(runtime)(effect, options)
export const unsafeRunCallback: {
<R>(runtime: Runtime.Runtime<R>): <A, E>(
effect: Effect.Effect<A, E, R>,
options?: Runtime.RunCallbackOptions<A, E> | undefined
) => (fiberId?: FiberId.FiberId, options?: Runtime.RunCallbackOptions<A, E> | undefined) => void
<R, A, E>(
runtime: Runtime.Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: Runtime.RunCallbackOptions<A, E> | undefined
): (fiberId?: FiberId.FiberId, options?: Runtime.RunCallbackOptions<A, E> | undefined) => void
} = makeDual((
runtime,
effect,
options: Runtime.RunCallbackOptions<any, any> = {}
): (fiberId?: FiberId.FiberId, options?: Runtime.RunCallbackOptions<any, any> | undefined) => void => {
const fiberRuntime = unsafeFork(runtime, effect, options)

if (options.onExit) {
fiberRuntime.addObserver((exit) => {
Expand All @@ -119,17 +153,19 @@ export const unsafeRunCallback = <R>(runtime: Runtime.Runtime<R>) =>
: undefined
}
)
}
})

/** @internal */
export const unsafeRunSync = <R>(runtime: Runtime.Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>): A => {
export const unsafeRunSync: {
<A, E, R>(runtime: Runtime.Runtime<R>, effect: Effect.Effect<A, E, R>): A
<R>(runtime: Runtime.Runtime<R>): <A, E>(effect: Effect.Effect<A, E, R>) => A
} = makeDual((runtime, effect) => {
const result = unsafeRunSyncExit(runtime)(effect)
if (result._tag === "Failure") {
throw fiberFailure(result.effect_instruction_i0)
} else {
return result.effect_instruction_i0
}
}
return result.effect_instruction_i0
})

class AsyncFiberExceptionImpl<A, E = never> extends Error implements Runtime.AsyncFiberException<A, E> {
readonly _tag = "AsyncFiberException"
Expand Down Expand Up @@ -229,31 +265,47 @@ const fastPath = <A, E, R>(effect: Effect.Effect<A, E, R>): Exit.Exit<A, E> | un
}

/** @internal */
export const unsafeRunSyncExit =
<R>(runtime: Runtime.Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>): Exit.Exit<A, E> => {
const op = fastPath(effect)
if (op) {
return op
}
const scheduler = new scheduler_.SyncScheduler()
const fiberRuntime = unsafeFork(runtime)(effect, { scheduler })
scheduler.flush()
const result = fiberRuntime.unsafePoll()
if (result) {
return result
}
return core.exitDie(core.capture(asyncFiberException(fiberRuntime), core.currentSpanFromFiber(fiberRuntime)))
export const unsafeRunSyncExit: {
<A, E, R>(runtime: Runtime.Runtime<R>, effect: Effect.Effect<A, E, R>): Exit.Exit<A, E>
<R>(runtime: Runtime.Runtime<R>): <A, E>(effect: Effect.Effect<A, E, R>) => Exit.Exit<A, E>
} = makeDual((runtime, effect) => {
const op = fastPath(effect)
if (op) {
return op
}
const scheduler = new scheduler_.SyncScheduler()
const fiberRuntime = unsafeFork(runtime)(effect, { scheduler })
scheduler.flush()
const result = fiberRuntime.unsafePoll()
if (result) {
return result
}
return core.exitDie(core.capture(asyncFiberException(fiberRuntime), core.currentSpanFromFiber(fiberRuntime)))
})

/** @internal */
export const unsafeRunPromise = <R>(runtime: Runtime.Runtime<R>) =>
<A, E>(
effect: Effect.Effect<A, E, R>,
export const unsafeRunPromise: {
<R>(runtime: Runtime.Runtime<R>): <A, E>(
effect: Effect.Effect<A, E, R>,
options?: {
readonly signal?: AbortSignal | undefined
} | undefined
) => Promise<A>
<R, A, E>(
runtime: Runtime.Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: {
readonly signal?: AbortSignal | undefined
} | undefined
): Promise<A>
} = makeDual((
runtime,
effect,
options?: {
readonly signal?: AbortSignal | undefined
} | undefined
): Promise<A> =>
unsafeRunPromiseExit(runtime)(effect, options).then((result) => {
) =>
unsafeRunPromiseExit(runtime, effect, options).then((result) => {
switch (result._tag) {
case OpCodes.OP_SUCCESS: {
return result.effect_instruction_i0
Expand All @@ -263,16 +315,29 @@ export const unsafeRunPromise = <R>(runtime: Runtime.Runtime<R>) =>
}
}
})
)

/** @internal */
export const unsafeRunPromiseExit = <R>(runtime: Runtime.Runtime<R>) =>
<A, E>(
effect: Effect.Effect<A, E, R>,
export const unsafeRunPromiseExit: {
<R>(
runtime: Runtime.Runtime<R>
): <A, E>(
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal | undefined } | undefined
) => Promise<Exit.Exit<A, E>>
<R, A, E>(
runtime: Runtime.Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal | undefined } | undefined
): Promise<Exit.Exit<A, E>>
} = makeDual((
runtime,
effect,
options?: {
readonly signal?: AbortSignal | undefined
} | undefined
): Promise<Exit.Exit<A, E>> =>
new Promise((resolve) => {
) =>
new Promise<Exit.Exit<any, any>>((resolve) => {
const op = fastPath(effect)
if (op) {
resolve(op)
Expand All @@ -291,6 +356,7 @@ export const unsafeRunPromiseExit = <R>(runtime: Runtime.Runtime<R>) =>
}
}
})
)

/** @internal */
export class RuntimeImpl<in R> implements Runtime.Runtime<R> {
Expand Down
Loading

0 comments on commit 96d29f5

Please sign in to comment.