diff --git a/packages/core/lib/main/QIO.ts b/packages/core/lib/main/QIO.ts index 99d30539..99b26b1e 100644 --- a/packages/core/lib/main/QIO.ts +++ b/packages/core/lib/main/QIO.ts @@ -24,7 +24,7 @@ const D = (scope: string, f: unknown, ...t: unknown[]) => * @typeparam E1 Possible errors that could be thrown by the program. * @typeparam R1 Environment needed to execute this instance. */ -export class QIO { +export class QIO { /** * Creates a new c instance with the provided environment */ @@ -39,8 +39,8 @@ export class QIO { fn: (A: CB, E: CB, R: R) => void ): QIO { return QIO.accessM((RR: R) => - QIO.uninterruptible((AA, EE) => fn(AA, EE, RR)) - ) + QIO.uninterruptible((AA, EE) => fn(AA as any, EE as any, RR)) + ) as any } /** * Effectfully creates a new c instance with the provided environment @@ -56,7 +56,7 @@ export class QIO { public static accessP( cb: (R: R1) => Promise ): QIO { - return QIO.env().chain(QIO.encaseP(cb)) + return QIO.env().chain(QIO.encaseP(cb)) as any } /** * Converts a [[QIO]] of a function into a [[QIO]] of a value. @@ -240,7 +240,7 @@ export class QIO { ab: (a: A1) => A2 ): QIO { if (fa.tag === Tag.Constant) { - return QIO.resolve(ab(fa.i0 as A1)) + return QIO.resolve(ab(fa.i0 as A1)) as any } return new QIO(Tag.Map, fa, ab) @@ -271,7 +271,7 @@ export class QIO { QIO.lift, E1>(() => List.empty()) ) ) - .map((_) => _.asArray.reverse()) + .map((_) => _.asArray.reverse()) as any } /** @@ -288,7 +288,7 @@ export class QIO { QIO.par(list.slice(0, N)).chain((l1) => itar(list.slice(N, list.length)).map((l2) => l1.concat(l2)) ) - ) + ) as any return itar(ios) } @@ -348,7 +348,7 @@ export class QIO { fList.chain((list) => f.map((value) => list.prepend(value))), QIO.lift, E1>(() => List.empty()).addEnv() ) - .map((_) => _.asArray) + .map((_) => _.asArray) as any } /** @@ -386,7 +386,7 @@ export class QIO { * ``` */ public static tryM(qio: () => QIO): QIO { - return QIO.flatten(QIO.lift(qio)) + return QIO.flatten(QIO.lift(qio)) as any } /** * Tries to run an function that returns a promise. @@ -448,7 +448,9 @@ export class QIO { * Safely converts an interuptable IO to non-interuptable one. */ public get asEither(): QIO, never, R1> { - return this.map(Either.right).catch((_) => QIO.resolve(Either.left(_))) + return this.map(Either.right).catch((_) => + QIO.resolve(Either.left(_)) + ) as any } /** * @ignore @@ -470,7 +472,7 @@ export class QIO { public get once(): QIO, never, R1> { return this.env.chain((env) => Await.of().map((AWT) => AWT.set(this.provide(env)).and(AWT.get)) - ) + ) as any } /** * Ignores the result of the c instance @@ -503,7 +505,7 @@ export class QIO { usage(a1) .fork() .chain((F) => F.await.and(release(a1)).chain((_) => F.join)) - ) + ) as any } public bracket_( @@ -534,13 +536,13 @@ export class QIO { * Ignores the original value of the c and resolves with the provided value */ public const(a: A2): QIO { - return this.and(QIO.resolve(a)) + return this.and(QIO.resolve(a)) as any } /** * Delays the execution of the [[QIO]] by the provided time. */ public delay(duration: number): QIO { - return QIO.timeout(this, duration).chain(Id) + return QIO.timeout(this, duration).chain(Id) as any } /** * Like [[QIO.tap]] but takes in an IO instead of a callback. @@ -555,7 +557,7 @@ export class QIO { public encase( fn: (A1: A1) => A2 ): QIO { - return this.chain(QIO.encase(fn)) + return this.chain(QIO.encase(fn)) as any } /** @@ -575,13 +577,15 @@ export class QIO { this.provide(ENV), RTM.configure(config === undefined ? RTM.config : config) ) - ) + ) as any } /** * Creates a separate [[Fiber]] with a different [[IRuntime]]. */ public forkWith(runtime: IRuntime): QIO, never, R1> { - return QIO.env().chain((ENV) => QIO.fork(this.provide(ENV), runtime)) + return QIO.env().chain((ENV) => + QIO.fork(this.provide(ENV), runtime) + ) as any } /** * Applies transformation on the success value of the c. @@ -607,19 +611,19 @@ export class QIO { * Provides the current instance of c the required env that is accessed effect-fully. */ public provideM(io: QIO): QIO { - return io.chain((ENV) => this.provide(ENV)) + return io.chain((ENV) => this.provide(ENV)) as any } /** * Provide only some of the environment */ public provideSome(fn: (R2: R0) => R1): QIO { - return QIO.accessM((r0: R0) => this.provide(fn(r0))) + return QIO.accessM((r0: R0) => this.provide(fn(r0))) as any } /** * Provide only some of the environment using an effect */ public provideSomeM(qio: QIO): QIO { - return qio.chain((_) => this.provide(_)) + return qio.chain((_) => this.provide(_)) as any } /** * Runs two IOs in parallel in returns the result of the first one. @@ -631,7 +635,7 @@ export class QIO { that, (E, F) => F.abort.const(E), (E, F) => F.abort.const(E) - ).chain((E) => QIO.fromExit(E)) + ).chain((E) => QIO.fromExit(E)) as any } /** @@ -660,14 +664,14 @@ export class QIO { return resume1.fork().and(resume2.fork()).and(done.get) }) - }) + }) as any } /** * Forcefully fails the current program with the provided error. */ public rejectWith(error: E2): QIO { - return this.and(QIO.reject(error)) + return this.and(QIO.reject(error)) as any } /** @@ -676,7 +680,7 @@ export class QIO { * and ignores it's result and continues. */ public tap(fn: (A: A1) => void): QIO { - return this.tapM(QIO.encase(fn)) + return this.tapM(QIO.encase(fn)) as any } /** @@ -737,4 +741,62 @@ export class QIO { ) ) } + *[Symbol.iterator](): Generator, A1, any> { + return (yield this) as any + } + + /** + * Allows for a do notation using generator functions + * @param fun Generator function + * + * @example + * // result: QIO + * const result = QIO.do(function*() { + * const { port } = yield* ask<{ port: number }>() + * if (port > 9999999) { + * yield* QIO.reject(Error("Port too big")) + * } + * return `port: ${port}` + * }) + */ + static do< + R, + Qio extends QIO, + L = Qio extends QIO ? L : never, + Env = Qio extends QIO ? E : never, + UnionEnv = UnionToIntersection + >(fun: () => Generator): QIO { + const iterator = fun() + const state = iterator.next() + function run( + state: IteratorYieldResult | IteratorReturnResult + ): QIO { + if (state.done) { + return QIO.resolve(state.value) + } + return state.value.chain((val) => { + const next = iterator.next(val as never) // typescript + return run(next) + }) + } + return run(state) + } } + +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( + k: infer I +) => void + ? I + : never + +/** + * Ask for an Env type. Useful in QIO.do + * + * Same as QIO.access((env: Env) => env) + * + * @example + * // test: QIO<{ name: string }, never, { name: string }> + * const test = ask<{ name: string }>() + */ + +export const ask = () => QIO.access((env: Env) => env)