Created
October 26, 2021 14:00
-
-
Save mikearnaldi/d9a9a74ae2b72f9b3146a64e1d3abaea to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import * as T from "@effect-ts/core/Effect" | |
import { pipe } from "@effect-ts/system/Function" | |
import type { _A } from "@effect-ts/system/Utils" | |
export type Flat<A> = { readonly [k in keyof A]: A[k] } extends infer X ? X : never | |
export function service<T extends symbol, X extends {}>( | |
t: T, | |
x: X | |
): Flat<X & { readonly serviceId: T }> { | |
// @ts-expect-error | |
return { ...x, serviceId: t } | |
} | |
export interface Tag<X extends AnyService> { | |
of: (_: Exclude<X, "serviceId">) => Has<X> | |
get: (_: Has<X>) => X | |
key: X["serviceId"] | |
} | |
export type Has<X extends AnyService> = { | |
[k in X["serviceId"]]: X | |
} | |
export interface Service<T extends symbol> { | |
readonly serviceId: T | |
} | |
export interface AnyService extends Service<symbol> {} | |
export function tag<X extends AnyService>(key: X["serviceId"]): Tag<X> { | |
return { | |
//@ts-expect-error | |
of: (_) => ({ [key]: service(key, _) }), | |
get: (_) => _[key], | |
key | |
} | |
} | |
export const CalcId = Symbol.for("@services/Calc") | |
export interface Calc extends _A<typeof makeCalc> {} | |
export const makeCalc = T.succeedWith(() => { | |
return service(CalcId, { | |
add: (x: number, y: number) => T.succeedWith(() => x + y) | |
}) | |
}) | |
export const Calc = tag<Calc>(CalcId) | |
export const LoggerId = Symbol.for("@services/Logger") | |
export interface Logger extends _A<typeof makeLogger> {} | |
export const makeLogger = T.succeedWith(() => { | |
return service(LoggerId, { | |
log: (msg: string) => | |
T.succeedWith(() => { | |
console.log(msg) | |
}) | |
}) | |
}) | |
export const Logger = tag<Logger>(LoggerId) | |
export declare function accessServiceM<T extends AnyService>( | |
s: Tag<T> | |
): <R, E, B>( | |
f: (a: T) => T.Effect<R, E, B>, | |
__trace?: string | |
) => T.Effect<R & Has<T>, E, B> | |
export function provideServiceM<T extends AnyService, R extends {}, E>( | |
service: T.Effect<R, E, T>, | |
__trace?: string | |
) { | |
return <R1 extends {}, E1, A1>( | |
ma: T.Effect<R1 & Has<T>, E1, A1> | |
): T.Effect<R & R1, E | E1, A1> => | |
pipe( | |
service, | |
T.chain((s) => T.provideSome_(ma, (r: R & R1) => ({ ...r, [s.serviceId]: s }))) | |
) | |
} | |
export function add(x: number, y: number) { | |
return accessServiceM(Calc)((_) => _.add(x, y)) | |
} | |
export function log(msg: string) { | |
return accessServiceM(Logger)((_) => _.log(msg)) | |
} | |
export const program = T.tuple(add(0, 1), log("ok")) | |
export const main = pipe( | |
program, | |
provideServiceM(makeCalc), | |
provideServiceM(makeLogger), | |
T.runPromise | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment