-
-
Notifications
You must be signed in to change notification settings - Fork 278
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add ManagedRuntime module, to make incremental adoption easier (#2211)
- Loading branch information
Showing
10 changed files
with
379 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
"effect": patch | ||
--- | ||
|
||
add ManagedRuntime module, to make incremental adoption easier | ||
|
||
You can use a ManagedRuntime to run Effect's that can use the | ||
dependencies from the given Layer. For example: | ||
|
||
```ts | ||
import { Console, Effect, Layer, ManagedRuntime } from "effect"; | ||
|
||
class Notifications extends Effect.Tag("Notifications")< | ||
Notifications, | ||
{ readonly notify: (message: string) => Effect.Effect<void> } | ||
>() { | ||
static Live = Layer.succeed(this, { | ||
notify: (message) => Console.log(message), | ||
}); | ||
} | ||
|
||
async function main() { | ||
const runtime = ManagedRuntime.make(Notifications.Live); | ||
await runtime.runPromise(Notifications.notify("Hello, world!")); | ||
await runtime.dispose(); | ||
} | ||
|
||
main(); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
"effect": patch | ||
--- | ||
|
||
add Layer.toRuntimeWithMemoMap api | ||
|
||
Similar to Layer.toRuntime, but allows you to share a Layer.MemoMap between | ||
layer builds. | ||
|
||
By sharing the MemoMap, layers are shared between each build - ensuring layers | ||
are only built once between multiple calls to Layer.toRuntimeWithMemoMap. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
/** | ||
* @since 2.0.0 | ||
*/ | ||
import type * as Effect from "./Effect.js" | ||
import type * as Exit from "./Exit.js" | ||
import type * as Fiber from "./Fiber.js" | ||
import * as internal from "./internal/managedRuntime.js" | ||
import type * as Layer from "./Layer.js" | ||
import type { Pipeable } from "./Pipeable.js" | ||
import type * as Runtime from "./Runtime.js" | ||
|
||
/** | ||
* @since 2.0.0 | ||
* @category models | ||
*/ | ||
export interface ManagedRuntime<in R, out ER> extends Pipeable { | ||
readonly memoMap: Layer.MemoMap | ||
readonly runtimeEffect: Effect.Effect<Runtime.Runtime<R>, ER> | ||
readonly runtime: () => Promise<Runtime.Runtime<R>> | ||
|
||
/** | ||
* Executes the effect using the provided Scheduler or using the global | ||
* Scheduler if not provided | ||
*/ | ||
readonly runFork: <A, E>( | ||
self: Effect.Effect<A, E, R>, | ||
options?: Runtime.RunForkOptions | ||
) => Fiber.RuntimeFiber<A, E | ER> | ||
|
||
/** | ||
* Executes the effect synchronously returning the exit. | ||
* | ||
* This method is effectful and should only be invoked at the edges of your | ||
* program. | ||
*/ | ||
readonly runSyncExit: <A, E>(effect: Effect.Effect<A, E, R>) => Exit.Exit<A, ER | E> | ||
|
||
/** | ||
* Executes the effect synchronously throwing in case of errors or async boundaries. | ||
* | ||
* This method is effectful and should only be invoked at the edges of your | ||
* program. | ||
*/ | ||
readonly runSync: <A, E>(effect: Effect.Effect<A, E, R>) => A | ||
|
||
/** | ||
* Executes the effect asynchronously, eventually passing the exit value to | ||
* the specified callback. | ||
* | ||
* This method is effectful and should only be invoked at the edges of your | ||
* program. | ||
*/ | ||
readonly runCallback: <A, E>( | ||
effect: Effect.Effect<A, E, R>, | ||
options?: Runtime.RunCallbackOptions<A, E | ER> | undefined | ||
) => Runtime.Cancel<A, E | ER> | ||
|
||
/** | ||
* Runs the `Effect`, returning a JavaScript `Promise` that will be resolved | ||
* with the value of the effect once the effect has been executed, or will be | ||
* rejected with the first error or exception throw by the effect. | ||
* | ||
* This method is effectful and should only be used at the edges of your | ||
* program. | ||
*/ | ||
readonly runPromise: <A, E>(effect: Effect.Effect<A, E, R>) => Promise<A> | ||
|
||
/** | ||
* Runs the `Effect`, returning a JavaScript `Promise` that will be resolved | ||
* with the `Exit` state of the effect once the effect has been executed. | ||
* | ||
* This method is effectful and should only be used at the edges of your | ||
* program. | ||
*/ | ||
readonly runPromiseExit: <A, E>(effect: Effect.Effect<A, E, R>) => Promise<Exit.Exit<A, ER | E>> | ||
|
||
/** | ||
* Dispose of the resources associated with the runtime. | ||
*/ | ||
readonly dispose: () => Promise<void> | ||
|
||
/** | ||
* Dispose of the resources associated with the runtime. | ||
*/ | ||
readonly disposeEffect: Effect.Effect<void, never, never> | ||
} | ||
|
||
/** | ||
* Convert a Layer into an ManagedRuntime, that can be used to run Effect's using | ||
* your services. | ||
* | ||
* @since 2.0.0 | ||
* @category runtime class | ||
* @example | ||
* import { Console, Effect, Layer, ManagedRuntime } from "effect" | ||
* | ||
* class Notifications extends Effect.Tag("Notifications")< | ||
* Notifications, | ||
* { readonly notify: (message: string) => Effect.Effect<void> } | ||
* >() { | ||
* static Live = Layer.succeed(this, { notify: (message) => Console.log(message) }) | ||
* } | ||
* | ||
* async function main() { | ||
* const runtime = ManagedRuntime.make(Notifications.Live) | ||
* await runtime.runPromise(Notifications.notify("Hello, world!")) | ||
* await runtime.dispose() | ||
* } | ||
* | ||
* main() | ||
*/ | ||
export const make: <R, E>( | ||
layer: Layer.Layer<R, E, never>, | ||
memoMap?: Layer.MemoMap | undefined | ||
) => ManagedRuntime<R, E> = internal.make |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import type * as Effect from "../Effect.js" | ||
import type { Exit } from "../Exit.js" | ||
import type * as Fiber from "../Fiber.js" | ||
import type * as Layer from "../Layer.js" | ||
import type { ManagedRuntime } from "../ManagedRuntime.js" | ||
import { pipeArguments } from "../Pipeable.js" | ||
import type * as Runtime from "../Runtime.js" | ||
import * as Scope from "../Scope.js" | ||
import * as effect from "./core-effect.js" | ||
import * as core from "./core.js" | ||
import * as fiberRuntime from "./fiberRuntime.js" | ||
import * as internalLayer from "./layer.js" | ||
import * as internalRuntime from "./runtime.js" | ||
|
||
interface ManagedRuntimeImpl<R, E> extends ManagedRuntime<R, E> { | ||
readonly scope: Scope.CloseableScope | ||
cachedRuntime: Runtime.Runtime<R> | undefined | ||
} | ||
|
||
function provide<R, ER, A, E>( | ||
managed: ManagedRuntimeImpl<R, ER>, | ||
effect: Effect.Effect<A, E, R> | ||
): Effect.Effect<A, E | ER> { | ||
return core.flatMap( | ||
managed.runtimeEffect, | ||
(rt) => | ||
core.withFiberRuntime((fiber) => { | ||
fiber.setFiberRefs(rt.fiberRefs) | ||
fiber._runtimeFlags = rt.runtimeFlags | ||
return core.provideContext(effect, rt.context) | ||
}) | ||
) | ||
} | ||
|
||
/** @internal */ | ||
export const make = <R, ER>( | ||
layer: Layer.Layer<R, ER, never>, | ||
memoMap?: Layer.MemoMap | ||
): ManagedRuntime<R, ER> => { | ||
memoMap = memoMap ?? internalLayer.unsafeMakeMemoMap() | ||
const scope = internalRuntime.unsafeRunSyncEffect(fiberRuntime.scopeMake()) | ||
const self: ManagedRuntimeImpl<R, ER> = { | ||
memoMap, | ||
scope, | ||
runtimeEffect: internalRuntime | ||
.unsafeRunSyncEffect( | ||
effect.memoize( | ||
core.tap( | ||
Scope.extend( | ||
internalLayer.toRuntimeWithMemoMap(layer, memoMap), | ||
scope | ||
), | ||
(rt) => { | ||
self.cachedRuntime = rt | ||
} | ||
) | ||
) | ||
), | ||
cachedRuntime: undefined, | ||
pipe() { | ||
return pipeArguments(this, arguments) | ||
}, | ||
runtime() { | ||
return self.cachedRuntime === undefined ? | ||
internalRuntime.unsafeRunPromiseEffect(self.runtimeEffect) : | ||
Promise.resolve(self.cachedRuntime) | ||
}, | ||
dispose(): Promise<void> { | ||
return internalRuntime.unsafeRunPromiseEffect(self.disposeEffect) | ||
}, | ||
disposeEffect: core.suspend(() => { | ||
;(self as any).runtime = core.die("ManagedRuntime disposed") | ||
self.cachedRuntime = undefined | ||
return Scope.close(self.scope, core.exitUnit) | ||
}), | ||
runFork<A, E>(effect: Effect.Effect<A, E, R>, options?: Runtime.RunForkOptions): Fiber.RuntimeFiber<A, E | ER> { | ||
return self.cachedRuntime === undefined ? | ||
internalRuntime.unsafeForkEffect(provide(self, effect), options) : | ||
internalRuntime.unsafeFork(self.cachedRuntime)(effect, options) | ||
}, | ||
runSyncExit<A, E>(effect: Effect.Effect<A, E, R>): Exit<A, E | ER> { | ||
return self.cachedRuntime === undefined ? | ||
internalRuntime.unsafeRunSyncExitEffect(provide(self, effect)) : | ||
internalRuntime.unsafeRunSyncExit(self.cachedRuntime)(effect) | ||
}, | ||
runSync<A, E>(effect: Effect.Effect<A, E, R>): A { | ||
return self.cachedRuntime === undefined ? | ||
internalRuntime.unsafeRunSyncEffect(provide(self, effect)) : | ||
internalRuntime.unsafeRunSync(self.cachedRuntime)(effect) | ||
}, | ||
runPromiseExit<A, E>(effect: Effect.Effect<A, E, R>): Promise<Exit<A, E | ER>> { | ||
return self.cachedRuntime === undefined ? | ||
internalRuntime.unsafeRunPromiseExitEffect(provide(self, effect)) : | ||
internalRuntime.unsafeRunPromiseExit(self.cachedRuntime)(effect) | ||
}, | ||
runCallback<A, E>( | ||
effect: Effect.Effect<A, E, R>, | ||
options?: Runtime.RunCallbackOptions<A, E | ER> | undefined | ||
): Runtime.Cancel<A, E | ER> { | ||
return self.cachedRuntime === undefined ? | ||
internalRuntime.unsafeRunCallback(internalRuntime.defaultRuntime)(provide(self, effect), options) : | ||
internalRuntime.unsafeRunCallback(self.cachedRuntime)(effect, options) | ||
}, | ||
runPromise<A, E>(effect: Effect.Effect<A, E, R>): Promise<A> { | ||
return self.cachedRuntime === undefined ? | ||
internalRuntime.unsafeRunPromiseEffect(provide(self, effect)) : | ||
internalRuntime.unsafeRunPromise(self.cachedRuntime)(effect) | ||
} | ||
} | ||
return self | ||
} |
Oops, something went wrong.