Skip to content

Instantly share code, notes, and snippets.

@tim-smart
Last active November 8, 2023 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tim-smart/7e15cccab9e87f2fb57b3614e4194114 to your computer and use it in GitHub Desktop.
Save tim-smart/7e15cccab9e87f2fb57b3614e4194114 to your computer and use it in GitHub Desktop.
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as Exit from "effect/Exit"
import * as Layer from "effect/Layer"
import * as Runtime from "effect/Runtime"
import * as Scope from "effect/Scope"
export interface RuntimeClass<R> extends AsyncDisposable {
readonly close: () => Promise<void>
readonly $effect: <E, A>(effect: Effect.Effect<R, E, A>) => Promise<A>
readonly $effectFn: <
F extends (...args: Array<any>) => Effect.Effect<R, any, any>,
>(
fn: F,
) => (...args: Parameters<F>) => Promise<Effect.Effect.Success<ReturnType<F>>>
readonly $service: <I extends R, S, A>(
tag: Context.Tag<I, S>,
fn: (service: S) => Effect.Effect<R, unknown, A>,
) => () => Promise<A>
readonly $serviceFn: <
I extends R,
S,
F extends (...args: Array<any>) => Effect.Effect<R, any, any>,
>(
tag: Context.Tag<I, S>,
fn: (service: S) => F,
) => (...args: Parameters<F>) => Promise<Effect.Effect.Success<ReturnType<F>>>
}
export interface RuntimeClassConstructor<Args extends Array<any>, R> {
new (...args: Args): RuntimeClass<R>
}
export function RuntimeClass<Args extends Array<any>, RE, R>(
layer: (...args: Args) => Layer.Layer<never, RE, R>,
): RuntimeClassConstructor<Args, R> {
return class {
#scope = Effect.runSync(Scope.make())
#runtime: Promise<Runtime.Runtime<R>>
constructor(...args: Args) {
this.#runtime = Effect.runPromise(
Layer.toRuntime(layer(...args)).pipe(Scope.extend(this.#scope)),
)
}
close(): Promise<void> {
return Effect.runPromise(Scope.close(this.#scope, Exit.unit))
}
[Symbol.asyncDispose]() {
return this.close()
}
$effect<E, A>(effect: Effect.Effect<R, E, A>): Promise<A> {
return this.#runtime.then(runtime => Runtime.runPromise(runtime)(effect))
}
$effectFn<F extends (...args: Array<any>) => Effect.Effect<R, any, any>>(
fn: F,
): (
...args: Parameters<F>
) => Promise<Effect.Effect.Success<ReturnType<F>>> {
return (...args) => this.$effect(fn(...args))
}
$service<I extends R, S, A>(
tag: Context.Tag<I, S>,
fn: (service: S) => Effect.Effect<R, unknown, A>,
): () => Promise<A> {
return () => this.$effect(Effect.flatMap(tag, _ => fn(_)))
}
$serviceFn<
I extends R,
S,
F extends (...args: Array<any>) => Effect.Effect<R, any, any>,
>(
tag: Context.Tag<I, S>,
fn: (service: S) => F,
): (
...args: Parameters<F>
) => Promise<Effect.Effect.Success<ReturnType<F>>> {
return (...args) => this.$effect(Effect.flatMap(tag, _ => fn(_)(args)))
}
}
}
interface Loggers {
readonly make: (name: string) => Logger
}
interface Logger {
readonly log: (message: string) => Effect.Effect<never, never, void>
}
const Loggers = Context.Tag<Loggers>("app/Loggers")
const LoggersLive = Layer.succeed(
Loggers,
Loggers.of({
make: name => ({
log: message => Effect.sync(() => console.log(`[${name}] ${message}`)),
}),
}),
)
// make runtime class
class LoggerRuntime extends RuntimeClass(() => LoggersLive) {
log = this.$serviceFn(Loggers, _ => _.make("test").log)
}
const runner = new LoggerRuntime()
runner.log("hello world")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment