import * as RA from "../../Array.js" import * as Cause from "../../Cause.js" import * as Chunk from "../../Chunk.js" import * as Context from "../../Context.js" import * as Effect from "../../Effect.js" import * as Either from "../../Either.js" import * as Exit from "../../Exit.js" import type * as FiberId from "../../FiberId.js" import type { LazyArg } from "../../Function.js" import { constFalse, constTrue, constVoid, dual, identity, pipe } from "../../Function.js" import * as Option from "../../Option.js" import type { Predicate, Refinement } from "../../Predicate.js" import * as predicate from "../../Predicate.js" import type * as STM from "../../STM.js" import type * as Types from "../../Types.js" import { yieldWrapGet } from "../../Utils.js" import * as effectCore from "../core.js" import * as core from "./core.js" import * as Journal from "./journal.js" import * as STMState from "./stmState.js" /** @internal */ export const acquireUseRelease = dual< ( use: (resource: A) => STM.STM, release: (resource: A) => STM.STM ) => ( acquire: STM.STM ) => Effect.Effect, ( acquire: STM.STM, use: (resource: A) => STM.STM, release: (resource: A) => STM.STM ) => Effect.Effect >(3, ( acquire: STM.STM, use: (resource: A) => STM.STM, release: (resource: A) => STM.STM ): Effect.Effect => Effect.uninterruptibleMask((restore) => { let state: STMState.STMState = STMState.running return pipe( restore( core.unsafeAtomically( acquire, (exit) => { state = STMState.done(exit) }, () => { state = STMState.interrupted } ) ), Effect.matchCauseEffect({ onFailure: (cause) => { if (STMState.isDone(state) && Exit.isSuccess(state.exit)) { return pipe( release(state.exit.value), Effect.matchCauseEffect({ onFailure: (cause2) => Effect.failCause(Cause.parallel(cause, cause2)), onSuccess: () => Effect.failCause(cause) }) ) } return Effect.failCause(cause) }, onSuccess: (a) => pipe( restore(use(a)), Effect.matchCauseEffect({ onFailure: (cause) => pipe( release(a), Effect.matchCauseEffect({ onFailure: (cause2) => Effect.failCause(Cause.parallel(cause, cause2)), onSuccess: () => Effect.failCause(cause) }) ), onSuccess: (a2) => pipe(release(a), Effect.as(a2)) }) ) }) ) })) /** @internal */ export const as = dual< (value: A2) => (self: STM.STM) => STM.STM, (self: STM.STM, value: A2) => STM.STM >(2, (self, value) => pipe(self, core.map(() => value))) /** @internal */ export const asSome = (self: STM.STM): STM.STM, E, R> => pipe(self, core.map(Option.some)) /** @internal */ export const asSomeError = (self: STM.STM): STM.STM, R> => pipe(self, mapError(Option.some)) /** @internal */ export const asVoid = (self: STM.STM): STM.STM => pipe(self, core.map(constVoid)) /** @internal */ export const attempt = (evaluate: LazyArg): STM.STM => suspend(() => { try { return core.succeed(evaluate()) } catch (defect) { return core.fail(defect) } }) export const bind = dual< ( tag: Exclude, f: (_: K) => STM.STM ) => (self: STM.STM) => STM.STM, E | E2, R | R2>, ( self: STM.STM, tag: Exclude, f: (_: K) => STM.STM ) => STM.STM, E | E2, R | R2> >(3, ( self: STM.STM, tag: Exclude, f: (_: K) => STM.STM ) => core.flatMap(self, (k) => core.map( f(k), (a): Types.MergeRecord => ({ ...k, [tag]: a } as any) ))) /* @internal */ export const bindTo = dual< (tag: N) => (self: STM.STM) => STM.STM< Record, E, R >, ( self: STM.STM, tag: N ) => STM.STM< Record, E, R > >( 2, (self: STM.STM, tag: N): STM.STM, E, R> => core.map(self, (a) => ({ [tag]: a } as Record)) ) /* @internal */ export const let_ = dual< ( tag: Exclude, f: (_: K) => A ) => (self: STM.STM) => STM.STM< Types.MergeRecord, E, R >, ( self: STM.STM, tag: Exclude, f: (_: K) => A ) => STM.STM< Types.MergeRecord, E, R > >(3, (self: STM.STM, tag: Exclude, f: (_: K) => A) => core.map( self, (k): Types.MergeRecord => ({ ...k, [tag]: f(k) } as any) )) /** @internal */ export const catchSome = dual< ( pf: (error: E) => Option.Option> ) => ( self: STM.STM ) => STM.STM, ( self: STM.STM, pf: (error: E) => Option.Option> ) => STM.STM >(2, ( self: STM.STM, pf: (error: E) => Option.Option> ): STM.STM => core.catchAll( self, (e): STM.STM => Option.getOrElse(pf(e), () => core.fail(e)) )) /** @internal */ export const catchTag = dual< ( k: K, f: (e: Extract) => STM.STM ) => (self: STM.STM) => STM.STM | E1, R | R1>, ( self: STM.STM, k: K, f: (e: Extract) => STM.STM ) => STM.STM | E1, R | R1> >(3, (self, k, f) => core.catchAll(self, (e) => { if ("_tag" in e && e["_tag"] === k) { return f(e as any) } return core.fail(e as any) })) /** @internal */ export const catchTags: { < E extends { _tag: string }, Cases extends { [K in E["_tag"]]+?: (error: Extract) => STM.STM } >( cases: Cases ): (self: STM.STM) => STM.STM< | A | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? A : never }[keyof Cases], | Exclude | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? E : never }[keyof Cases], | R | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? R : never }[keyof Cases] > < R, E extends { _tag: string }, A, Cases extends { [K in E["_tag"]]+?: (error: Extract) => STM.STM } >( self: STM.STM, cases: Cases ): STM.STM< | A | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? A : never }[keyof Cases], | Exclude | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? E : never }[keyof Cases], | R | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? R : never }[keyof Cases] > } = dual(2, (self, cases) => core.catchAll(self, (e: any) => { const keys = Object.keys(cases) if ("_tag" in e && keys.includes(e["_tag"])) { return cases[e["_tag"]](e as any) } return core.fail(e as any) })) /** @internal */ export const check = (predicate: LazyArg): STM.STM => suspend(() => predicate() ? void_ : core.retry) /** @internal */ export const collect = dual< (pf: (a: A) => Option.Option) => (self: STM.STM) => STM.STM, (self: STM.STM, pf: (a: A) => Option.Option) => STM.STM >(2, (self, pf) => collectSTM( self, (a) => Option.map(pf(a), core.succeed) )) /** @internal */ export const collectSTM = dual< ( pf: (a: A) => Option.Option> ) => ( self: STM.STM ) => STM.STM, ( self: STM.STM, pf: (a: A) => Option.Option> ) => STM.STM >(2, (self, pf) => core.matchSTM(self, { onFailure: core.fail, onSuccess: (a) => { const option = pf(a) return Option.isSome(option) ? option.value : core.retry } })) /** @internal */ export const commitEither = (self: STM.STM): Effect.Effect => Effect.flatten(core.commit(either(self))) /** @internal */ export const cond = ( predicate: LazyArg, error: LazyArg, result: LazyArg ): STM.STM => { return suspend( () => predicate() ? core.sync(result) : core.failSync(error) ) } /** @internal */ export const either = (self: STM.STM): STM.STM, never, R> => match(self, { onFailure: Either.left, onSuccess: Either.right }) /** @internal */ export const eventually = (self: STM.STM): STM.STM => core.matchSTM(self, { onFailure: () => eventually(self), onSuccess: core.succeed }) /** @internal */ export const every = dual< ( predicate: (a: Types.NoInfer) => STM.STM ) => (iterable: Iterable) => STM.STM, (iterable: Iterable, predicate: (a: A) => STM.STM) => STM.STM >( 2, ( iterable: Iterable, predicate: (a: A) => STM.STM ): STM.STM => core.flatMap(core.sync(() => iterable[Symbol.iterator]()), (iterator) => { const loop: STM.STM = suspend(() => { const next = iterator.next() if (next.done) { return core.succeed(true) } return pipe( predicate(next.value), core.flatMap((bool) => bool ? loop : core.succeed(bool)) ) }) return loop }) ) /** @internal */ export const exists = dual< ( predicate: (a: Types.NoInfer) => STM.STM ) => (iterable: Iterable) => STM.STM, (iterable: Iterable, predicate: (a: A) => STM.STM) => STM.STM >( 2, (iterable: Iterable, predicate: (a: A) => STM.STM): STM.STM => core.flatMap(core.sync(() => iterable[Symbol.iterator]()), (iterator) => { const loop: STM.STM = suspend(() => { const next = iterator.next() if (next.done) { return core.succeed(false) } return core.flatMap( predicate(next.value), (bool) => bool ? core.succeed(bool) : loop ) }) return loop }) ) /** @internal */ export const fiberId: STM.STM = core.effect((_, fiberId) => fiberId) /** @internal */ export const filter = dual< ( predicate: (a: Types.NoInfer) => STM.STM ) => (iterable: Iterable) => STM.STM, E, R>, (iterable: Iterable, predicate: (a: A) => STM.STM) => STM.STM, E, R> >( 2, (iterable: Iterable, predicate: (a: A) => STM.STM): STM.STM, E, R> => Array.from(iterable).reduce( (acc, curr) => pipe( acc, core.zipWith(predicate(curr), (as, p) => { if (p) { as.push(curr) return as } return as }) ), core.succeed([]) as STM.STM, E, R> ) ) /** @internal */ export const filterNot = dual< ( predicate: (a: Types.NoInfer) => STM.STM ) => (iterable: Iterable) => STM.STM, E, R>, (iterable: Iterable, predicate: (a: A) => STM.STM) => STM.STM, E, R> >( 2, (iterable: Iterable, predicate: (a: A) => STM.STM): STM.STM, E, R> => filter(iterable, (a) => negate(predicate(a))) ) /** @internal */ export const filterOrDie: { ( refinement: Refinement, B>, defect: LazyArg ): (self: STM.STM) => STM.STM ( predicate: Predicate>, defect: LazyArg ): (self: STM.STM) => STM.STM ( self: STM.STM, refinement: Refinement, defect: LazyArg ): STM.STM (self: STM.STM, predicate: Predicate, defect: LazyArg): STM.STM } = dual( 3, (self: STM.STM, predicate: Predicate, defect: LazyArg): STM.STM => filterOrElse(self, predicate, () => core.dieSync(defect)) ) /** @internal */ export const filterOrDieMessage: { ( refinement: Refinement, B>, message: string ): (self: STM.STM) => STM.STM (predicate: Predicate>, message: string): (self: STM.STM) => STM.STM (self: STM.STM, refinement: Refinement, message: string): STM.STM (self: STM.STM, predicate: Predicate, message: string): STM.STM } = dual( 3, (self: STM.STM, predicate: Predicate, message: string): STM.STM => filterOrElse(self, predicate, () => core.dieMessage(message)) ) /** @internal */ export const filterOrElse: { ( refinement: Refinement, B>, orElse: (a: Types.NoInfer) => STM.STM ): (self: STM.STM) => STM.STM ( predicate: Predicate>, orElse: (a: Types.NoInfer) => STM.STM ): (self: STM.STM) => STM.STM ( self: STM.STM, refinement: Refinement, orElse: (a: A) => STM.STM ): STM.STM ( self: STM.STM, predicate: Predicate, orElse: (a: A) => STM.STM ): STM.STM } = dual( 3, ( self: STM.STM, predicate: Predicate, orElse: (a: A) => STM.STM ): STM.STM => core.flatMap(self, (a): STM.STM => predicate(a) ? core.succeed(a) : orElse(a)) ) /** @internal */ export const filterOrFail: { ( refinement: Refinement, B>, orFailWith: (a: Types.NoInfer) => E2 ): (self: STM.STM) => STM.STM ( predicate: Predicate>, orFailWith: (a: Types.NoInfer) => E2 ): (self: STM.STM) => STM.STM ( self: STM.STM, refinement: Refinement, orFailWith: (a: A) => E2 ): STM.STM (self: STM.STM, predicate: Predicate, orFailWith: (a: A) => E2): STM.STM } = dual( 3, (self: STM.STM, predicate: Predicate, orFailWith: (a: A) => E2): STM.STM => filterOrElse( self, predicate, (a) => core.failSync(() => orFailWith(a)) ) ) /** @internal */ export const flatten = (self: STM.STM, E, R>): STM.STM => core.flatMap(self, identity) /** @internal */ export const flip = (self: STM.STM): STM.STM => core.matchSTM(self, { onFailure: core.succeed, onSuccess: core.fail }) /** @internal */ export const flipWith = dual< ( f: (stm: STM.STM) => STM.STM ) => ( self: STM.STM ) => STM.STM, ( self: STM.STM, f: (stm: STM.STM) => STM.STM ) => STM.STM >(2, (self, f) => flip(f(flip(self)))) /** @internal */ export const match = dual< (options: { readonly onFailure: (error: E) => A2 readonly onSuccess: (value: A) => A3 }) => (self: STM.STM) => STM.STM, (self: STM.STM, options: { readonly onFailure: (error: E) => A2 readonly onSuccess: (value: A) => A3 }) => STM.STM >(2, (self, { onFailure, onSuccess }) => core.matchSTM(self, { onFailure: (e) => core.succeed(onFailure(e)), onSuccess: (a) => core.succeed(onSuccess(a)) })) /** @internal */ export const forEach = dual< { (f: (a: A) => STM.STM, options?: { readonly discard?: false | undefined }): (elements: Iterable) => STM.STM, E, R> (f: (a: A) => STM.STM, options: { readonly discard: true }): (elements: Iterable) => STM.STM }, { (elements: Iterable, f: (a: A) => STM.STM, options?: { readonly discard?: false | undefined }): STM.STM, E, R> (elements: Iterable, f: (a: A) => STM.STM, options: { readonly discard: true }): STM.STM } >( (args) => predicate.isIterable(args[0]), (iterable: Iterable, f: (a: A) => STM.STM, options?: { readonly discard?: boolean | undefined }): STM.STM => { if (options?.discard) { return pipe( core.sync(() => iterable[Symbol.iterator]()), core.flatMap((iterator) => { const loop: STM.STM = suspend(() => { const next = iterator.next() if (next.done) { return void_ } return pipe(f(next.value), core.flatMap(() => loop)) }) return loop }) ) } return suspend(() => RA.fromIterable(iterable).reduce( (acc, curr) => core.zipWith(acc, f(curr), (array, elem) => { array.push(elem) return array }), core.succeed([]) as STM.STM, E, R> ) ) } ) /** @internal */ export const fromEither = (either: Either.Either): STM.STM => { switch (either._tag) { case "Left": { return core.fail(either.left) } case "Right": { return core.succeed(either.right) } } } /** @internal */ export const fromOption = (option: Option.Option): STM.STM> => Option.match(option, { onNone: () => core.fail(Option.none()), onSome: core.succeed }) /** * Inspired by https://github.com/tusharmath/qio/pull/22 (revised) * @internal */ export const gen: typeof STM.gen = (...args) => suspend(() => { const f = (args.length === 1) ? args[0] : args[1].bind(args[0]) const iterator = f(pipe) const state = iterator.next() const run = ( state: IteratorYieldResult | IteratorReturnResult ): STM.STM => state.done ? core.succeed(state.value) : core.flatMap(yieldWrapGet(state.value) as any, (val: any) => run(iterator.next(val as never))) return run(state) }) /** @internal */ export const head = (self: STM.STM, E, R>): STM.STM, R> => pipe( self, core.matchSTM({ onFailure: (e) => core.fail(Option.some(e)), onSuccess: (a) => { const i = a[Symbol.iterator]() const res = i.next() if (res.done) { return core.fail(Option.none()) } else { return core.succeed(res.value) } } }) ) /** @internal */ export const if_ = dual< ( options: { readonly onTrue: STM.STM readonly onFalse: STM.STM } ) => ( self: STM.STM | boolean ) => STM.STM, { ( self: boolean, options: { readonly onTrue: STM.STM readonly onFalse: STM.STM } ): STM.STM ( self: STM.STM, options: { readonly onTrue: STM.STM readonly onFalse: STM.STM } ): STM.STM } >( (args) => typeof args[0] === "boolean" || core.isSTM(args[0]), ( self: STM.STM | boolean, { onFalse, onTrue }: { readonly onTrue: STM.STM readonly onFalse: STM.STM } ) => { if (typeof self === "boolean") { return self ? onTrue : onFalse } return core.flatMap(self, (bool): STM.STM => bool ? onTrue : onFalse) } ) /** @internal */ export const ignore = (self: STM.STM): STM.STM => match(self, { onFailure: () => void_, onSuccess: () => void_ }) /** @internal */ export const isFailure = (self: STM.STM): STM.STM => match(self, { onFailure: constTrue, onSuccess: constFalse }) /** @internal */ export const isSuccess = (self: STM.STM): STM.STM => match(self, { onFailure: constFalse, onSuccess: constTrue }) /** @internal */ export const iterate = ( initial: Z, options: { readonly while: (z: Z) => boolean readonly body: (z: Z) => STM.STM } ): STM.STM => iterateLoop(initial, options.while, options.body) const iterateLoop = ( initial: Z, cont: (z: Z) => boolean, body: (z: Z) => STM.STM ): STM.STM => { if (cont(initial)) { return pipe( body(initial), core.flatMap((z) => iterateLoop(z, cont, body)) ) } return core.succeed(initial) } /** @internal */ export const loop: { ( initial: Z, options: { readonly while: (z: Z) => boolean readonly step: (z: Z) => Z readonly body: (z: Z) => STM.STM readonly discard?: false | undefined } ): STM.STM, E, R> ( initial: Z, options: { readonly while: (z: Z) => boolean readonly step: (z: Z) => Z readonly body: (z: Z) => STM.STM readonly discard: true } ): STM.STM } = ( initial: Z, options: { readonly while: (z: Z) => boolean readonly step: (z: Z) => Z readonly body: (z: Z) => STM.STM readonly discard?: boolean | undefined } ): STM.STM => options.discard ? loopDiscardLoop(initial, options.while, options.step, options.body) : core.map(loopLoop(initial, options.while, options.step, options.body), (a) => Array.from(a)) const loopLoop = ( initial: Z, cont: (z: Z) => boolean, inc: (z: Z) => Z, body: (z: Z) => STM.STM ): STM.STM, E, R> => { if (cont(initial)) { return pipe( body(initial), core.flatMap((a) => pipe(loopLoop(inc(initial), cont, inc, body), core.map(Chunk.append(a)))) ) } return core.succeed(Chunk.empty()) } const loopDiscardLoop = ( initial: Z, cont: (z: Z) => boolean, inc: (z: Z) => Z, body: (z: Z) => STM.STM ): STM.STM => { if (cont(initial)) { return pipe( body(initial), core.flatMap(() => loopDiscardLoop(inc(initial), cont, inc, body)) ) } return void_ } /** @internal */ export const mapAttempt = dual< (f: (a: A) => B) => (self: STM.STM) => STM.STM, (self: STM.STM, f: (a: A) => B) => STM.STM >(2, (self: STM.STM, f: (a: A) => B): STM.STM => core.matchSTM(self, { onFailure: (e) => core.fail(e), onSuccess: (a) => attempt(() => f(a)) })) /** @internal */ export const mapBoth = dual< (options: { readonly onFailure: (error: E) => E2 readonly onSuccess: (value: A) => A2 }) => (self: STM.STM) => STM.STM, (self: STM.STM, options: { readonly onFailure: (error: E) => E2 readonly onSuccess: (value: A) => A2 }) => STM.STM >(2, (self, { onFailure, onSuccess }) => core.matchSTM(self, { onFailure: (e) => core.fail(onFailure(e)), onSuccess: (a) => core.succeed(onSuccess(a)) })) /** @internal */ export const mapError = dual< (f: (error: E) => E2) => (self: STM.STM) => STM.STM, (self: STM.STM, f: (error: E) => E2) => STM.STM >(2, (self, f) => core.matchSTM(self, { onFailure: (e) => core.fail(f(e)), onSuccess: core.succeed })) /** @internal */ export const merge = (self: STM.STM): STM.STM => core.matchSTM(self, { onFailure: (e) => core.succeed(e), onSuccess: core.succeed }) /** @internal */ export const mergeAll = dual< (zero: A2, f: (a2: A2, a: A) => A2) => (iterable: Iterable>) => STM.STM, (iterable: Iterable>, zero: A2, f: (a2: A2, a: A) => A2) => STM.STM >( 3, (iterable: Iterable>, zero: A2, f: (a2: A2, a: A) => A2): STM.STM => suspend(() => Array.from(iterable).reduce( (acc, curr) => pipe(acc, core.zipWith(curr, f)), core.succeed(zero) as STM.STM ) ) ) /** @internal */ export const negate = (self: STM.STM): STM.STM => pipe(self, core.map((b) => !b)) /** @internal */ export const none = (self: STM.STM, E, R>): STM.STM, R> => core.matchSTM(self, { onFailure: (e) => core.fail(Option.some(e)), onSuccess: Option.match({ onNone: () => void_, onSome: () => core.fail(Option.none()) }) }) /** @internal */ export const option = (self: STM.STM): STM.STM, never, R> => match(self, { onFailure: () => Option.none(), onSuccess: Option.some }) /** @internal */ export const orDie = (self: STM.STM): STM.STM => pipe(self, orDieWith(identity)) /** @internal */ export const orDieWith = dual< (f: (error: E) => unknown) => (self: STM.STM) => STM.STM, (self: STM.STM, f: (error: E) => unknown) => STM.STM >(2, (self, f) => pipe(self, mapError(f), core.catchAll(core.die))) /** @internal */ export const orElse = dual< (that: LazyArg>) => (self: STM.STM) => STM.STM, (self: STM.STM, that: LazyArg>) => STM.STM >( 2, (self: STM.STM, that: LazyArg>): STM.STM => core.flatMap(core.effect>((journal) => Journal.prepareResetJournal(journal)), (reset) => pipe( core.orTry(self, () => core.flatMap(core.sync(reset), that)), core.catchAll(() => core.flatMap(core.sync(reset), that)) )) ) /** @internal */ export const orElseEither = dual< ( that: LazyArg> ) => ( self: STM.STM ) => STM.STM, E2, R2 | R>, ( self: STM.STM, that: LazyArg> ) => STM.STM, E2, R2 | R> >( 2, ( self: STM.STM, that: LazyArg> ): STM.STM, E2, R2 | R> => orElse(core.map(self, Either.left), () => core.map(that(), Either.right)) ) /** @internal */ export const orElseFail = dual< (error: LazyArg) => (self: STM.STM) => STM.STM, (self: STM.STM, error: LazyArg) => STM.STM >( 2, (self: STM.STM, error: LazyArg): STM.STM => orElse(self, () => core.failSync(error)) ) /** @internal */ export const orElseOptional = dual< ( that: LazyArg, R2>> ) => ( self: STM.STM, R> ) => STM.STM, R2 | R>, ( self: STM.STM, R>, that: LazyArg, R2>> ) => STM.STM, R2 | R> >( 2, ( self: STM.STM, R>, that: LazyArg, R2>> ): STM.STM, R2 | R> => core.catchAll( self, Option.match({ onNone: that, onSome: (e) => core.fail(Option.some(e)) }) ) ) /** @internal */ export const orElseSucceed = dual< (value: LazyArg) => (self: STM.STM) => STM.STM, (self: STM.STM, value: LazyArg) => STM.STM >( 2, (self: STM.STM, value: LazyArg): STM.STM => orElse(self, () => core.sync(value)) ) /** @internal */ export const provideContext = dual< (env: Context.Context) => (self: STM.STM) => STM.STM, (self: STM.STM, env: Context.Context) => STM.STM >(2, (self, env) => core.mapInputContext(self, (_: Context.Context) => env)) /** @internal */ export const provideSomeContext = dual< (context: Context.Context) => (self: STM.STM) => STM.STM>, (self: STM.STM, context: Context.Context) => STM.STM> >(2, ( self: STM.STM, context: Context.Context ): STM.STM> => core.mapInputContext( self, (parent: Context.Context>): Context.Context => Context.merge(parent, context) as any )) /** @internal */ export const provideService = dual< ( tag: Context.Tag, resource: Types.NoInfer ) => ( self: STM.STM ) => STM.STM>, ( self: STM.STM, tag: Context.Tag, resource: Types.NoInfer ) => STM.STM> >(3, (self, tag, resource) => provideServiceSTM(self, tag, core.succeed(resource))) /** @internal */ export const provideServiceSTM = dual< ( tag: Context.Tag, stm: STM.STM, E1, R1> ) => ( self: STM.STM ) => STM.STM>, ( self: STM.STM, tag: Context.Tag, stm: STM.STM, E1, R1> ) => STM.STM> >(3, ( self: STM.STM, tag: Context.Tag, stm: STM.STM, E1, R1> ): STM.STM> => core.contextWithSTM((env: Context.Context>) => core.flatMap( stm, (service) => provideContext( self, Context.add(env, tag, service) as Context.Context ) ) )) /** @internal */ export const reduce = dual< (zero: S, f: (s: S, a: A) => STM.STM) => (iterable: Iterable) => STM.STM, (iterable: Iterable, zero: S, f: (s: S, a: A) => STM.STM) => STM.STM >( 3, (iterable: Iterable, zero: S, f: (s: S, a: A) => STM.STM): STM.STM => suspend(() => Array.from(iterable).reduce( (acc, curr) => pipe(acc, core.flatMap((s) => f(s, curr))), core.succeed(zero) as STM.STM ) ) ) /** @internal */ export const reduceAll = dual< ( initial: STM.STM, f: (x: A, y: A) => A ) => ( iterable: Iterable> ) => STM.STM, ( iterable: Iterable>, initial: STM.STM, f: (x: A, y: A) => A ) => STM.STM >(3, ( iterable: Iterable>, initial: STM.STM, f: (x: A, y: A) => A ): STM.STM => suspend(() => Array.from(iterable).reduce( (acc, curr) => pipe(acc, core.zipWith(curr, f)), initial as STM.STM ) )) /** @internal */ export const reduceRight = dual< (zero: S, f: (s: S, a: A) => STM.STM) => (iterable: Iterable) => STM.STM, (iterable: Iterable, zero: S, f: (s: S, a: A) => STM.STM) => STM.STM >( 3, (iterable: Iterable, zero: S, f: (s: S, a: A) => STM.STM): STM.STM => suspend(() => Array.from(iterable).reduceRight( (acc, curr) => pipe(acc, core.flatMap((s) => f(s, curr))), core.succeed(zero) as STM.STM ) ) ) /** @internal */ export const refineOrDie = dual< (pf: (error: E) => Option.Option) => (self: STM.STM) => STM.STM, (self: STM.STM, pf: (error: E) => Option.Option) => STM.STM >(2, (self, pf) => refineOrDieWith(self, pf, identity)) /** @internal */ export const refineOrDieWith = dual< ( pf: (error: E) => Option.Option, f: (error: E) => unknown ) => ( self: STM.STM ) => STM.STM, ( self: STM.STM, pf: (error: E) => Option.Option, f: (error: E) => unknown ) => STM.STM >(3, (self, pf, f) => core.catchAll( self, (e) => Option.match(pf(e), { onNone: () => core.die(f(e)), onSome: core.fail }) )) /** @internal */ export const reject = dual< (pf: (a: A) => Option.Option) => (self: STM.STM) => STM.STM, (self: STM.STM, pf: (a: A) => Option.Option) => STM.STM >(2, (self, pf) => rejectSTM( self, (a) => Option.map(pf(a), core.fail) )) /** @internal */ export const rejectSTM = dual< ( pf: (a: A) => Option.Option> ) => ( self: STM.STM ) => STM.STM, ( self: STM.STM, pf: (a: A) => Option.Option> ) => STM.STM >(2, (self, pf) => core.flatMap(self, (a) => Option.match(pf(a), { onNone: () => core.succeed(a), onSome: core.flatMap(core.fail) }))) /** @internal */ export const repeatUntil = dual< (predicate: Predicate) => (self: STM.STM) => STM.STM, (self: STM.STM, predicate: Predicate) => STM.STM >(2, (self, predicate) => repeatUntilLoop(self, predicate)) const repeatUntilLoop = (self: STM.STM, predicate: Predicate): STM.STM => core.flatMap(self, (a) => predicate(a) ? core.succeed(a) : repeatUntilLoop(self, predicate)) /** @internal */ export const repeatWhile = dual< (predicate: Predicate) => (self: STM.STM) => STM.STM, (self: STM.STM, predicate: Predicate) => STM.STM >(2, (self, predicate) => repeatWhileLoop(self, predicate)) const repeatWhileLoop = (self: STM.STM, predicate: Predicate): STM.STM => core.flatMap(self, (a) => predicate(a) ? repeatWhileLoop(self, predicate) : core.succeed(a)) /** @internal */ export const replicate = dual< (n: number) => (self: STM.STM) => Array>, (self: STM.STM, n: number) => Array> >(2, (self, n) => Array.from({ length: n }, () => self)) /** @internal */ export const replicateSTM = dual< (n: number) => (self: STM.STM) => STM.STM, E, R>, (self: STM.STM, n: number) => STM.STM, E, R> >(2, (self, n) => all(replicate(self, n))) /** @internal */ export const replicateSTMDiscard = dual< (n: number) => (self: STM.STM) => STM.STM, (self: STM.STM, n: number) => STM.STM >(2, (self, n) => all(replicate(self, n), { discard: true })) /** @internal */ export const retryUntil = dual< { (refinement: Refinement, B>): (self: STM.STM) => STM.STM (predicate: Predicate): (self: STM.STM) => STM.STM }, { (self: STM.STM, refinement: Refinement): STM.STM (self: STM.STM, predicate: Predicate): STM.STM } >( 2, (self: STM.STM, predicate: Predicate) => core.matchSTM(self, { onFailure: core.fail, onSuccess: (a) => predicate(a) ? core.succeed(a) : core.retry }) ) /** @internal */ export const retryWhile = dual< (predicate: Predicate) => (self: STM.STM) => STM.STM, (self: STM.STM, predicate: Predicate) => STM.STM >( 2, (self, predicate) => core.matchSTM(self, { onFailure: core.fail, onSuccess: (a) => !predicate(a) ? core.succeed(a) : core.retry }) ) /** @internal */ export const partition = dual< ( f: (a: A) => STM.STM ) => ( elements: Iterable ) => STM.STM<[excluded: Array, satisfying: Array], never, R>, ( elements: Iterable, f: (a: A) => STM.STM ) => STM.STM<[excluded: Array, satisfying: Array], never, R> >(2, (elements, f) => pipe( forEach(elements, (a) => either(f(a))), core.map((as) => effectCore.partitionMap(as, identity)) )) /** @internal */ export const some = (self: STM.STM, E, R>): STM.STM, R> => core.matchSTM(self, { onFailure: (e) => core.fail(Option.some(e)), onSuccess: Option.match({ onNone: () => core.fail(Option.none()), onSome: core.succeed }) }) /* @internal */ export const all = (( input: Iterable | Record, options?: STM.All.Options ): STM.STM => { if (Symbol.iterator in input) { return forEach(input, identity, options as any) } else if (options?.discard) { return forEach(Object.values(input), identity, options as any) } return core.map( forEach( Object.entries(input), ([_, e]) => core.map(e, (a) => [_, a] as const) ), (values) => { const res = {} for (const [k, v] of values) { ;(res as any)[k] = v } return res } ) }) as STM.All.Signature /** @internal */ export const succeedNone: STM.STM> = core.succeed(Option.none()) /** @internal */ export const succeedSome = (value: A): STM.STM> => core.succeed(Option.some(value)) /** @internal */ export const summarized = dual< ( summary: STM.STM, f: (before: A2, after: A2) => A3 ) => ( self: STM.STM ) => STM.STM<[A3, A], E2 | E, R2 | R>, ( self: STM.STM, summary: STM.STM, f: (before: A2, after: A2) => A3 ) => STM.STM<[A3, A], E2 | E, R2 | R> >(3, (self, summary, f) => core.flatMap(summary, (start) => core.flatMap(self, (value) => core.map( summary, (end) => [f(start, end), value] )))) /** @internal */ export const suspend = (evaluate: LazyArg>): STM.STM => flatten(core.sync(evaluate)) /** @internal */ export const tap: { (f: (a: A) => STM.STM): (self: STM.STM) => STM.STM (self: STM.STM, f: (a: A) => STM.STM): STM.STM } = dual( 2, (self: STM.STM, f: (a: A) => STM.STM): STM.STM => core.flatMap(self, (a) => as(f(a), a)) ) /** @internal */ export const tapBoth = dual< ( options: { readonly onFailure: (error: XE) => STM.STM readonly onSuccess: (value: XA) => STM.STM } ) => ( self: STM.STM ) => STM.STM, ( self: STM.STM, options: { readonly onFailure: (error: XE) => STM.STM readonly onSuccess: (value: XA) => STM.STM } ) => STM.STM >(2, (self, { onFailure, onSuccess }) => core.matchSTM(self, { onFailure: (e) => pipe(onFailure(e as any), core.zipRight(core.fail(e))), onSuccess: (a) => pipe(onSuccess(a as any), as(a)) })) /** @internal */ export const tapError: { ( f: (error: Types.NoInfer) => STM.STM ): (self: STM.STM) => STM.STM (self: STM.STM, f: (error: E) => STM.STM): STM.STM } = dual( 2, (self: STM.STM, f: (error: E) => STM.STM): STM.STM => core.matchSTM(self, { onFailure: (e) => core.zipRight(f(e), core.fail(e)), onSuccess: core.succeed }) ) /** @internal */ export const try_: { (options: { readonly try: LazyArg readonly catch: (u: unknown) => E }): STM.STM (try_: LazyArg): STM.STM } = ( arg: LazyArg | { readonly try: LazyArg readonly catch: (u: unknown) => E } ) => { const evaluate = typeof arg === "function" ? arg : arg.try return suspend(() => { try { return core.succeed(evaluate()) } catch (error) { return core.fail("catch" in arg ? arg.catch(error) : error) } }) } /** @internal */ const void_: STM.STM = core.succeed(void 0) export { /** @internal */ void_ as void } /** @internal */ export const unless = dual< (predicate: LazyArg) => (self: STM.STM) => STM.STM, E, R>, (self: STM.STM, predicate: LazyArg) => STM.STM, E, R> >(2, (self, predicate) => suspend( () => predicate() ? succeedNone : asSome(self) )) /** @internal */ export const unlessSTM = dual< ( predicate: STM.STM ) => ( self: STM.STM ) => STM.STM, E2 | E, R2 | R>, ( self: STM.STM, predicate: STM.STM ) => STM.STM, E2 | E, R2 | R> >(2, (self, predicate) => core.flatMap( predicate, (bool) => bool ? succeedNone : asSome(self) )) /** @internal */ export const unsome = (self: STM.STM, R>): STM.STM, E, R> => core.matchSTM(self, { onFailure: Option.match({ onNone: () => core.succeed(Option.none()), onSome: core.fail }), onSuccess: (a) => core.succeed(Option.some(a)) }) /** @internal */ export const validateAll = dual< ( f: (a: A) => STM.STM ) => ( elements: Iterable ) => STM.STM, RA.NonEmptyArray, R>, ( elements: Iterable, f: (a: A) => STM.STM ) => STM.STM, RA.NonEmptyArray, R> >( 2, (elements, f) => core.flatMap(partition(elements, f), ([errors, values]) => RA.isNonEmptyArray(errors) ? core.fail(errors) : core.succeed(values)) ) /** @internal */ export const validateFirst = dual< (f: (a: A) => STM.STM) => (elements: Iterable) => STM.STM, R>, (elements: Iterable, f: (a: A) => STM.STM) => STM.STM, R> >(2, (elements, f) => flip(forEach(elements, (a) => flip(f(a))))) /** @internal */ export const when = dual< (predicate: LazyArg) => (self: STM.STM) => STM.STM, E, R>, (self: STM.STM, predicate: LazyArg) => STM.STM, E, R> >(2, (self, predicate) => suspend( () => predicate() ? asSome(self) : succeedNone )) /** @internal */ export const whenSTM = dual< ( predicate: STM.STM ) => ( self: STM.STM ) => STM.STM, E2 | E, R2 | R>, ( self: STM.STM, predicate: STM.STM ) => STM.STM, E2 | E, R2 | R> >(2, (self, predicate) => core.flatMap( predicate, (bool) => bool ? asSome(self) : succeedNone ))