Hyperlinkv0.8.0-beta.28

ManagedRuntime

ManagedRuntime.makeconsteffect/ManagedRuntime.ts:273
<R, ER>(
  layer: Layer.Layer<R, ER, never>,
  options?: { readonly memoMap?: Layer.MemoMap | undefined } | undefined
): ManagedRuntime<R, ER>

Creates a ManagedRuntime from a layer.

When to use

Use to create a reusable runtime from a Layer for application entry points or integration code that runs many effects without rebuilding services.

Details

The layer is built lazily on first use and its context is cached for subsequent runs. Resources acquired by the layer are owned by the runtime and are released when dispose or disposeEffect is run. options.memoMap can be used to share layer memoization with other layer builds.

Gotchas

Dispose the runtime when it is no longer needed. A runtime cannot be reused after disposal.

Example (Creating a managed runtime)

import { Context, Effect, Layer, ManagedRuntime } from "effect"

class Notifications extends Context.Service<Notifications, {
  readonly notify: (message: string) => Effect.Effect<void>
}>()("Notifications") {
  static readonly layer = Layer.succeed(this)({
    notify: Effect.fn("Notifications.notify")((message) =>
      Effect.sync(() => console.log(message))
    )
  })
}

const runtime = ManagedRuntime.make(Notifications.layer)

const program = Effect.flatMap(
  Notifications,
  (_) => _.notify("Hello, world!")
).pipe(Effect.ensuring(runtime.disposeEffect))

runtime.runPromise(program)
// Hello, world!
export const make = <R, ER>(
  layer: Layer.Layer<R, ER, never>,
  options?: {
    readonly memoMap?: Layer.MemoMap | undefined
  } | undefined
): ManagedRuntime<R, ER> => {
  const memoMap = options?.memoMap ?? Layer.makeMemoMapUnsafe()
  const scope = Scope.makeUnsafe("parallel")
  const layerScope = Scope.forkUnsafe(scope, "sequential")
  const defaultRunOptions: Effect.RunOptions = {
    onFiberStart: Fiber.runIn(scope)
  }
  const mergeRunOptions = <O extends Effect.RunOptions>(options?: O): O =>
    options
      ? {
        ...options,
        onFiberStart: options.onFiberStart ?
          (fiber) => {
            defaultRunOptions.onFiberStart!(fiber)
            options.onFiberStart!(fiber)
          } :
          defaultRunOptions.onFiberStart
      }
      : defaultRunOptions as O
  let buildFiber: Fiber.Fiber<Context.Context<R>, ER> | undefined
  const contextEffect = Effect.withFiber<Context.Context<R>, ER>((fiber) => {
    if (!buildFiber) {
      buildFiber = Effect.runFork(
        Effect.tap(
          Layer.buildWithMemoMap(layer, memoMap, layerScope),
          (context) =>
            Effect.sync(() => {
              self.cachedContext = context
            })
        ),
        { ...defaultRunOptions, scheduler: fiber.currentScheduler }
      )
    }
    return Effect.flatten(Fiber.await(buildFiber))
  })
  const self: ManagedRuntime<R, ER> = {
    [TypeId]: TypeId,
    memoMap,
    scope,
    contextEffect: contextEffect,
    cachedContext: undefined,
    context() {
      return self.cachedContext === undefined ?
        Effect.runPromise(self.contextEffect) :
        Promise.resolve(self.cachedContext)
    },
    dispose(): Promise<void> {
      return Effect.runPromise(self.disposeEffect)
    },
    disposeEffect: Effect.suspend(() => {
      ;(self as Mutable<ManagedRuntime<R, ER>>).contextEffect = Effect.die("ManagedRuntime disposed")
      self.cachedContext = undefined
      return Scope.close(self.scope, Exit.void)
    }),
    runFork<A, E>(effect: Effect.Effect<A, E, R>, options?: Effect.RunOptions): Fiber.Fiber<A, E | ER> {
      return self.cachedContext === undefined ?
        Effect.runFork(provide(self, effect), mergeRunOptions(options)) :
        Effect.runForkWith(self.cachedContext)(effect, mergeRunOptions(options))
    },
    runCallback<A, E>(
      effect: Effect.Effect<A, E, R>,
      options?: Effect.RunOptions & {
        readonly onExit: (exit: Exit.Exit<A, E | ER>) => void
      }
    ): (interruptor?: number | undefined) => void {
      return self.cachedContext === undefined ?
        Effect.runCallback(provide(self, effect), mergeRunOptions(options)) :
        Effect.runCallbackWith(self.cachedContext)(effect, mergeRunOptions(options))
    },
    runSyncExit<A, E>(effect: Effect.Effect<A, E, R>): Exit.Exit<A, E | ER> {
      return self.cachedContext === undefined ?
        Effect.runSyncExit(provide(self, effect)) :
        Effect.runSyncExitWith(self.cachedContext)(effect)
    },
    runSync<A, E>(effect: Effect.Effect<A, E, R>): A {
      return self.cachedContext === undefined ?
        Effect.runSync(provide(self, effect)) :
        Effect.runSyncWith(self.cachedContext)(effect)
    },
    runPromiseExit<A, E>(effect: Effect.Effect<A, E, R>, options?: Effect.RunOptions): Promise<Exit.Exit<A, E | ER>> {
      return self.cachedContext === undefined ?
        Effect.runPromiseExit(provide(self, effect), mergeRunOptions(options)) :
        Effect.runPromiseExitWith(self.cachedContext)(effect, mergeRunOptions(options))
    },
    runPromise<A, E>(effect: Effect.Effect<A, E, R>, options?: {
      readonly signal?: AbortSignal | undefined
    }): Promise<A> {
      return self.cachedContext === undefined ?
        Effect.runPromise(provide(self, effect), mergeRunOptions(options)) :
        Effect.runPromiseWith(self.cachedContext)(effect, mergeRunOptions(options))
    }
  }
  return self
}