UNPKG

@effect-ts/system

Version:

Effect-TS is a zero dependency set of libraries to write highly productive, purely functional TypeScript at scale.

321 lines (311 loc) 9 kB
// ets_tracing: off import * as T from "../../../../Effect/index.js" import * as E from "../../../../Either/index.js" import * as Ex from "../../../../Exit/index.js" import * as F from "../../../../Fiber/index.js" import { pipe } from "../../../../Function/index.js" import * as M from "../../../../Managed/index.js" import * as MH from "../_internal/mergeHelpers.js" import * as C from "../core.js" import * as FromInput from "./fromInput.js" import * as ToPull from "./toPull.js" import * as Unwrap from "./unwrap.js" import * as UnwrapManaged from "./unwrapManaged.js" import * as ZipRight from "./zipRight.js" /** * Returns a new channel, which is the merge of this channel and the specified channel, where * the behavior of the returned channel on left or right early termination is decided by the * specified `leftDone` and `rightDone` merge decisions. */ export function mergeWith_< Env, Env1, InErr, InErr1, InElem, InElem1, InDone, InDone1, OutErr, OutErr1, OutErr2, OutErr3, OutElem, OutElem1, OutDone, OutDone1, OutDone2, OutDone3 >( self: C.Channel<Env, InErr, InElem, InDone, OutErr, OutElem, OutDone>, that: C.Channel<Env1, InErr1, InElem1, InDone1, OutErr1, OutElem1, OutDone1>, leftDone: ( ex: Ex.Exit<OutErr, OutDone> ) => MH.MergeDecision<Env1, OutErr1, OutDone1, OutErr2, OutDone2>, rightDone: ( ex: Ex.Exit<OutErr1, OutDone1> ) => MH.MergeDecision<Env1, OutErr, OutDone, OutErr3, OutDone3> ): C.Channel< Env1 & Env, InErr & InErr1, InElem & InElem1, InDone & InDone1, OutErr2 | OutErr3, OutElem | OutElem1, OutDone2 | OutDone3 > { const m = pipe( M.do, M.bind("input", () => T.toManaged( C.makeSingleProducerAsyncInput< InErr & InErr1, InElem & InElem1, InDone & InDone1 >() ) ), M.let("queueReader", ({ input }) => FromInput.fromInput(input)), M.bind("pullL", ({ queueReader }) => ToPull.toPull(queueReader[">>>"](self))), M.bind("pullR", ({ queueReader }) => ToPull.toPull(queueReader[">>>"](that))), M.chain(({ input, pullL, pullR, queueReader }) => T.toManaged( T.transplant((graft) => T.succeed({ input, pullL: graft(pullL), pullR: graft(pullR), queueReader }) ) ) ), M.map(({ input, pullL, pullR }) => { type MergeState = MH.MergeState< Env & Env1, OutErr, OutErr1, OutErr2 | OutErr3, OutElem | OutElem1, OutDone, OutDone1, OutDone2 | OutDone3 > const handleSide = <Err, Done, Err2, Done2>( exit: Ex.Exit<Err, E.Either<Done, OutElem | OutElem1>>, fiber: F.Fiber<Err2, E.Either<Done2, OutElem | OutElem1>>, pull: T.Effect<Env & Env1, Err, E.Either<Done, OutElem | OutElem1>> ) => ( done: ( ex: Ex.Exit<Err, Done> ) => MH.MergeDecision< Env & Env1, Err2, Done2, OutErr2 | OutErr3, OutDone2 | OutDone3 >, both: ( f1: F.Fiber<Err, E.Either<Done, OutElem | OutElem1>>, f2: F.Fiber<Err2, E.Either<Done2, OutElem | OutElem1>> ) => MergeState, single: ( f: ( ex: Ex.Exit<Err2, Done2> ) => T.Effect<Env & Env1, OutErr2 | OutErr3, OutDone2 | OutDone3> ) => MergeState ): T.Effect< Env & Env1, never, C.Channel< Env & Env1, unknown, unknown, unknown, OutErr2 | OutErr3, OutElem | OutElem1, OutDone2 | OutDone3 > > => { const onDecision = ( decision: MH.MergeDecision< Env & Env1, Err2, Done2, OutErr2 | OutErr3, OutDone2 | OutDone3 > ): T.Effect< unknown, never, C.Channel< Env & Env1, unknown, unknown, unknown, OutErr2 | OutErr3, OutElem | OutElem1, OutDone2 | OutDone3 > > => { MH.concrete(decision) if (decision._typeId === MH.DoneTypeId) { return T.succeed( C.fromEffect(T.zipRight_(F.interrupt(fiber), decision.io)) ) } else { return T.map_( fiber.await, Ex.fold( (cause) => C.fromEffect(decision.f(Ex.halt(cause))), E.fold( (z) => C.fromEffect(decision.f(Ex.succeed(z))), (elem) => ZipRight.zipRight_(C.write(elem), go(single(decision.f))) ) ) ) } } return Ex.fold_( exit, (failure) => onDecision(done(Ex.halt(failure))), E.fold( (z) => onDecision(done(Ex.succeed(z))), (elem) => T.map_(T.forkDaemon(pull), (leftFiber) => ZipRight.zipRight_(C.write(elem), go(both(leftFiber, fiber))) ) ) ) } const go = ( state: MergeState ): C.Channel< Env & Env1, unknown, unknown, unknown, OutErr2 | OutErr3, OutElem | OutElem1, OutDone2 | OutDone3 > => { if (state._typeId === MH.BothRunningTypeId) { const lj: T.Effect< Env1, OutErr, E.Either<OutDone, OutElem | OutElem1> > = F.join(state.left) const rj: T.Effect< Env1, OutErr1, E.Either<OutDone1, OutElem | OutElem1> > = F.join(state.right) return Unwrap.unwrap( T.raceWith_( lj, rj, (leftEx, _) => handleSide(leftEx, state.right, pullL)( leftDone, (l, r) => new MH.BothRunning(l, r), (_) => new MH.LeftDone(_) ), (rightEx, _) => handleSide(rightEx, state.left, pullR)( rightDone, (l, r) => new MH.BothRunning(r, l), (_) => new MH.RightDone(_) ) ) ) } else if (state._typeId === MH.LeftDoneTypeId) { return Unwrap.unwrap( T.map_( T.result(pullR), Ex.fold( (cause) => C.fromEffect(state.f(Ex.halt(cause))), E.fold( (z) => C.fromEffect(state.f(Ex.succeed(z))), (elem) => ZipRight.zipRight_(C.write(elem), go(new MH.LeftDone(state.f))) ) ) ) ) } else { return Unwrap.unwrap( T.map_( T.result(pullL), Ex.fold( (cause) => C.fromEffect(state.f(Ex.halt(cause))), E.fold( (z) => C.fromEffect(state.f(Ex.succeed(z))), (elem) => ZipRight.zipRight_(C.write(elem), go(new MH.RightDone(state.f))) ) ) ) ) } } return pipe( C.fromEffect( T.zipWith_( T.forkDaemon(pullL), T.forkDaemon(pullR), (a, b): MergeState => new MH.BothRunning< unknown, OutErr, OutErr1, unknown, OutElem | OutElem1, OutDone, OutDone1, unknown >(a, b) ) ), C.chain(go), C.embedInput(input) ) }) ) return UnwrapManaged.unwrapManaged(m) } /** * Returns a new channel, which is the merge of this channel and the specified channel, where * the behavior of the returned channel on left or right early termination is decided by the * specified `leftDone` and `rightDone` merge decisions. * * @ets_data_first mergeWith_ */ export function mergeWith< Env1, InErr1, InElem1, InDone1, OutErr, OutErr1, OutErr2, OutErr3, OutElem1, OutDone, OutDone1, OutDone2, OutDone3 >( that: C.Channel<Env1, InErr1, InElem1, InDone1, OutErr1, OutElem1, OutDone1>, leftDone: ( ex: Ex.Exit<OutErr, OutDone> ) => MH.MergeDecision<Env1, OutErr1, OutDone1, OutErr2, OutDone2>, rightDone: ( ex: Ex.Exit<OutErr1, OutDone1> ) => MH.MergeDecision<Env1, OutErr, OutDone, OutErr3, OutDone3> ) { return <Env, InErr, InElem, InDone, OutElem>( self: C.Channel<Env, InErr, InElem, InDone, OutErr, OutElem, OutDone> ) => mergeWith_(self, that, leftDone, rightDone) }