Skip to content

Instantly share code, notes, and snippets.

@mikearnaldi
Created October 26, 2021 14:00
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 mikearnaldi/d9a9a74ae2b72f9b3146a64e1d3abaea to your computer and use it in GitHub Desktop.
Save mikearnaldi/d9a9a74ae2b72f9b3146a64e1d3abaea to your computer and use it in GitHub Desktop.
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