UNPKG

@typed/fp

Version:

Data Structures and Resources for fp-ts

654 lines 20.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.onDispose = exports.collectEvents = exports.createSink = exports.tupled = exports.bindTo = exports.Do = exports.Filterable = exports.Compactable = exports.zero = exports.Alternative = exports.race = exports.Alt = exports.fromResumeK = exports.chainResumeK = exports.chainFirstResumeK = exports.fromResume = exports.FromResume = exports.fromTask = exports.FromTask = exports.fromIO = exports.FromIO = exports.getConcurrentChainRec = exports.mergeConcurrentlyRec = exports.SwitchRec = exports.switchRec = exports.ChainRec = exports.chainRec = exports.Monad = exports.bind = exports.chainFirst = exports.Chain = exports.getApplicativeMonoid = exports.Applicative = exports.getApplySemigroup = exports.apT = exports.apSecond = exports.apS = exports.apFirst = exports.Apply = exports.of = exports.Pointed = exports.Functor = exports.filterMap = exports.partition = exports.partitionMap = exports.separate = exports.compact = exports.getMonoid = exports.URI = exports.createCallbackTask = void 0; exports.mergeFirst = exports.switchFirst = exports.keyed = exports.mergeMapWhen = exports.exhaustMapLatest = exports.exhaustLatest = exports.combineStruct = exports.combineAll = void 0; const tslib_1 = require("tslib"); /** * Stream is an extension of @most/core with additional * fp-ts instances as well as additional combinators for interoperation with other data * structures in @typed/fp and fp-ts. * * A large goal of @typed/fp is to expand the `fp-ts` ecosystem to include * [@most/core](https://github.com/mostjs/core) for a Reactive programming style, including * derivatives such as [ReaderStream](./ReaderStream.ts.md), [ReaderStreamEither](./ReaderStreamEither.ts.md), * [StateReaderStreamEither](./StateReaderStreamEither.ts.md) and a few others. It's the fastest push-based * reactive library in JS period. The performance characteristics are due to it's architecture of getting out of * the way of the computations you need to perform. It's also the first experience I had with FP. For instance, * Most utilizes `Functor` laws to remove unneeded machinery through function composition improving runtime * performance amongst other optimizations. * * See the [@most/core Documentation](https://mostcore.readthedocs.io/en/latest/) for the remaining API * exposed by this module. Both @most/core + @most/types are re-exported from this module * @since 0.9.2 */ const M = (0, tslib_1.__importStar)(require("@most/core")); const disposable_1 = require("@most/disposable"); const scheduler_1 = require("@most/scheduler"); const App = (0, tslib_1.__importStar)(require("fp-ts/Applicative")); const Ap = (0, tslib_1.__importStar)(require("fp-ts/Apply")); const CH = (0, tslib_1.__importStar)(require("fp-ts/Chain")); const Either_1 = require("fp-ts/Either"); const function_1 = require("fp-ts/function"); const Functor_1 = require("fp-ts/Functor"); const O = (0, tslib_1.__importStar)(require("fp-ts/Option")); const RA = (0, tslib_1.__importStar)(require("fp-ts/ReadonlyArray")); const RM = (0, tslib_1.__importStar)(require("fp-ts/ReadonlyMap")); const Tuple2_1 = require("fp-ts/Tuple2"); const Adapter_1 = require("./Adapter"); const Disposable_1 = require("./Disposable"); const Eq_1 = require("./Eq"); const FRe = (0, tslib_1.__importStar)(require("./FromResume")); const R = (0, tslib_1.__importStar)(require("./Resume")); const S = (0, tslib_1.__importStar)(require("./struct")); /** * Convert an IO<Disposable> into a Most.js Task * @since 0.9.2 * @category Constructor */ function createCallbackTask(cb, onError) { const disposable = (0, Disposable_1.settable)(); return { run(t) { if (!disposable.isDisposed()) { disposable.addDisposable(cb(t)); } }, error(_, e) { disposable.dispose(); if (onError) { return onError(e); } throw e; }, dispose: disposable.dispose, }; } exports.createCallbackTask = createCallbackTask; /** * @since 0.9.2 * @category URI */ exports.URI = '@most/core/Stream'; /** * Create a Stream monoid where concat is a parallel merge. */ /** * @since 0.9.2 * @category Typeclass Constructor */ const getMonoid = () => { return { concat: M.merge, empty: M.empty(), }; }; exports.getMonoid = getMonoid; /** * Filter Option's from within a Stream */ /** * @since 0.9.2 * @category Combinator */ const compact = (stream) => M.map((s) => s.value, M.filter(O.isSome, stream)); exports.compact = compact; /** * Separate left and right values */ /** * @since 0.9.2 * @category Combinator */ const separate = (stream) => { const s = M.multicast(stream); const left = (0, function_1.pipe)(s, M.filter(Either_1.isLeft), M.map((l) => l.left)); const right = (0, function_1.pipe)(s, M.filter(Either_1.isRight), M.map((r) => r.right)); return { left, right }; }; exports.separate = separate; /** * @since 0.9.2 * @category Combinator */ const partitionMap = (f) => (fa) => (0, exports.separate)(M.map(f, fa)); exports.partitionMap = partitionMap; /** * @since 0.9.2 * @category Combinator */ const partition = (predicate) => (0, exports.partitionMap)((a) => (predicate(a) ? (0, Either_1.right)(a) : (0, Either_1.left)(a))); exports.partition = partition; /** * @since 0.9.2 * @category Combinator */ const filterMap = (f) => (fa) => (0, exports.compact)(M.map(f, fa)); exports.filterMap = filterMap; /** * @since 0.9.2 * @category Instance */ exports.Functor = { map: M.map, }; /** * @since 0.9.2 * @category Instance */ exports.Pointed = { of: M.now, }; /** * @since 0.9.2 * @category Constructor */ exports.of = exports.Pointed.of; /** * @since 0.9.2 * @category Instance */ exports.Apply = { ...exports.Functor, ap: M.ap, }; /** * @since 0.9.2 * @category Combinator */ exports.apFirst = Ap.apFirst(exports.Apply); /** * @since 0.9.2 * @category Combinator */ exports.apS = Ap.apS(exports.Apply); /** * @since 0.9.2 * @category Combinator */ exports.apSecond = Ap.apSecond(exports.Apply); /** * @since 0.9.2 * @category Combinator */ exports.apT = Ap.apT(exports.Apply); /** * @since 0.9.2 * @category Typeclass Constructor */ exports.getApplySemigroup = Ap.getApplySemigroup(exports.Apply); /** * @since 0.9.2 * @category Instance */ exports.Applicative = { ...exports.Apply, ...exports.Pointed, }; /** * @since 0.9.2 * @category Typeclass Constructor */ exports.getApplicativeMonoid = App.getApplicativeMonoid(exports.Applicative); /** * @since 0.9.2 * @category Instance */ exports.Chain = { ...exports.Functor, chain: M.chain, }; /** * @since 0.9.2 * @category Combinator */ exports.chainFirst = CH.chainFirst(exports.Chain); /** * @since 0.9.2 * @category Combinator */ exports.bind = CH.bind(exports.Chain); /** * @since 0.9.2 * @category Instance */ exports.Monad = { ...exports.Chain, ...exports.Pointed, }; /** * @since 0.9.2 * @category Combinator */ const chainRec = (f) => (value) => (0, function_1.pipe)(value, f, M.delay(0), M.chain((0, Either_1.match)((0, function_1.flow)((0, exports.chainRec)(f)), M.now))); exports.chainRec = chainRec; /** * @since 0.9.2 * @category Instance */ exports.ChainRec = { chainRec: exports.chainRec, }; /** * @since 0.9.2 * @category Combinator */ const switchRec = (f) => (value) => (0, function_1.pipe)(value, f, M.map((0, Either_1.match)((0, exports.switchRec)(f), M.now)), M.switchLatest); exports.switchRec = switchRec; /** * @since 0.9.2 * @category Instance */ exports.SwitchRec = { chainRec: exports.switchRec, }; /** * @since 0.9.2 * @category Combinator */ const mergeConcurrentlyRec = (concurrency) => (f) => (value) => (0, function_1.pipe)(value, f, M.map((0, Either_1.match)((0, exports.mergeConcurrentlyRec)(concurrency)(f), M.now)), M.mergeConcurrently(concurrency)); exports.mergeConcurrentlyRec = mergeConcurrentlyRec; /** * @since 0.9.2 * @category Typeclass Constructor */ const getConcurrentChainRec = (concurrency) => ({ chainRec: (0, exports.mergeConcurrentlyRec)(concurrency), }); exports.getConcurrentChainRec = getConcurrentChainRec; /** * @since 0.9.2 * @category Instance */ exports.FromIO = { fromIO: (f) => exports.Functor.map(f)(M.now(undefined)), }; /** * @since 0.9.2 * @category Constructor */ exports.fromIO = exports.FromIO.fromIO; const applyTask = (task) => M.ap(M.now(task), M.now(void 0)); /** * @since 0.9.2 * @category Instance */ exports.FromTask = { ...exports.FromIO, fromTask: (0, function_1.flow)(applyTask, M.awaitPromises), }; /** * @since 0.9.2 * @category Constructor */ exports.fromTask = exports.FromTask.fromTask; /** * @since 0.9.2 * @category Instance */ exports.FromResume = { fromResume: (resume) => M.newStream((sink, scheduler) => { const run = () => (0, function_1.pipe)(resume, R.start((a) => { sink.event(scheduler.currentTime(), a); sink.end(scheduler.currentTime()); })); const onError = (error) => sink.error(scheduler.currentTime(), error); return (0, scheduler_1.asap)(createCallbackTask(run, onError), scheduler); }), }; /** * @since 0.9.2 * @category Constructor */ exports.fromResume = exports.FromResume.fromResume; /** * @since 0.9.2 * @category Combinator */ exports.chainFirstResumeK = FRe.chainFirstResumeK(exports.FromResume, exports.Chain); /** * @since 0.9.2 * @category Combinator */ exports.chainResumeK = FRe.chainResumeK(exports.FromResume, exports.Chain); /** * @since 0.9.2 * @category Constructor */ exports.fromResumeK = FRe.fromResumeK(exports.FromResume); /** * @since 0.9.2 * @category Instance */ exports.Alt = { ...exports.Functor, alt: (f) => (fa) => M.take(1, M.merge(fa, f())), // race the 2 streams }; /** * @since 0.9.2 * @category Combinator */ exports.race = exports.Alt.alt; /** * @since 0.9.2 * @category Instance */ exports.Alternative = { ...exports.Alt, zero: M.empty, }; /** * @since 0.9.2 * @category Constructor */ exports.zero = exports.Alternative.zero; /** * @since 0.9.2 * @category Instance */ exports.Compactable = { compact: exports.compact, separate: exports.separate, }; /** * @since 0.9.2 * @category Instance */ exports.Filterable = { partitionMap: exports.partitionMap, partition: exports.partition, filterMap: exports.filterMap, filter: M.filter, }; /** * @since 0.9.2 * @category Constructor */ exports.Do = (0, function_1.pipe)(null, M.now, M.map(Object.create)); /** * @since 0.9.2 * @category Combinator */ exports.bindTo = (0, Functor_1.bindTo)(exports.Functor); /** * @since 0.9.2 * @category Combinator */ exports.tupled = (0, Functor_1.tupled)(exports.Functor); const emptySink = { event: function_1.constVoid, error: function_1.constVoid, end: function_1.constVoid, }; /** * @since 0.9.2 * @category Constructor */ const createSink = (sink = {}) => ({ ...emptySink, ...sink, }); exports.createSink = createSink; /** * @since 0.9.2 * @category Combinator */ const collectEvents = (scheduler) => (stream) => { const events = []; return M.runEffects(M.tap((a) => events.push(a), stream), scheduler).then(() => events); }; exports.collectEvents = collectEvents; /** * @since 0.9.2 * @category Combinator */ const onDispose = (disposable) => (stream) => M.newStream((sink, scheduler) => (0, Disposable_1.disposeBoth)(stream.run(sink, scheduler), disposable)); exports.onDispose = onDispose; /** * @since 0.9.2 * @category Combinator */ const combineAll = (...streams) => (0, function_1.pipe)(streams, M.combineArray(Array)); exports.combineAll = combineAll; /** * @since 0.11.0 * @category Combinator */ const combineStruct = (streams) => (0, function_1.pipe)((0, exports.combineAll)(...(0, function_1.pipe)(Object.entries(streams), RA.map(([k, stream]) => (0, function_1.pipe)(stream, M.map((v) => S.make(k, v)))))), M.map((o) => Object.assign({}, ...o))); exports.combineStruct = combineStruct; /** * @since 0.9.2 * @category Combinator */ const exhaustLatest = (stream) => new ExhaustLatest(stream); exports.exhaustLatest = exhaustLatest; /** * @since 0.9.2 * @category Combinator */ const exhaustMapLatest = (f) => (stream) => (0, function_1.pipe)(stream, M.map(f), exports.exhaustLatest); exports.exhaustMapLatest = exhaustMapLatest; class ExhaustLatest { constructor(stream) { this.stream = stream; } run(sink, scheduler) { const s = new ExhaustLatestSink(sink, scheduler); return (0, Disposable_1.disposeBoth)(this.stream.run(s, scheduler), s); } } class ExhaustLatestSink { constructor(sink, scheduler) { this.sink = sink; this.scheduler = scheduler; this.latest = O.none; this.disposable = (0, disposable_1.disposeNone)(); this.sampling = false; this.shouldResample = false; this.finished = false; this.event = (t, stream) => { this.latest = O.some(stream); if (this.sampling) { this.shouldResample = true; return; } this.sampling = true; this.shouldResample = false; this.disposable = stream.run(this.innerSink, (0, scheduler_1.schedulerRelativeTo)(t, this.scheduler)); }; this.error = (t, e) => { this.dispose(); this.sink.error(t, e); }; this.end = (_) => { this.finished = true; }; this.dispose = () => { this.disposable.dispose(); this.sampling = false; this.shouldResample = false; }; this.innerSink = { event: (t, a) => sink.event(t, a), error: (t, e) => this.error(t, e), end: (t) => { this.sampling = false; if (this.shouldResample && O.isSome(this.latest)) { this.event(scheduler.currentTime(), this.latest.value); return; } if (this.finished) { this.dispose(); sink.end(t); } }, }; } } /** * Using the provided Eq mergeMapWhen conditionally applies a Kliesli arrow * to the values within an Array when they are added and any values removed * from the array will be disposed of immediately * @since 0.9.2 * @category Combinator */ const mergeMapWhen = (Eq = Eq_1.deepEqualsEq) => (f) => (values) => new MergeMapWhen(Eq, f, values); exports.mergeMapWhen = mergeMapWhen; class MergeMapWhen { constructor(Eq, f, values) { this.Eq = Eq; this.f = f; this.values = values; } run(sink, scheduler) { const s = new MergeMapWhenSink(sink, scheduler, this); return (0, Disposable_1.disposeBoth)(this.values.run(s, scheduler), s); } } class MergeMapWhenSink { constructor(sink, scheduler, source) { this.sink = sink; this.scheduler = scheduler; this.source = source; this.disposables = new Map(); this.values = new Map(); this.current = []; this.finished = false; this.referenceCount = 0; this.disposable = (0, disposable_1.disposeNone)(); this.event = (t, values) => { const removed = (0, function_1.pipe)(this.current, this.findDifference(values)); const added = (0, function_1.pipe)(values, this.findDifference(this.current)); this.current = values; // Clean up all the removed keys (0, function_1.pipe)(removed, RA.map(this.findDisposable), RA.compact, disposable_1.disposeAll).dispose(); removed.forEach((r) => this.values.delete(r)); // Subscribe to all the newly added values added.forEach((a) => this.disposables.set(a, this.runInner(t, a))); this.emitIfReady(); }; this.error = (t, error) => { this.dispose(); this.sink.error(t, error); }; this.end = (t) => { this.finished = true; this.endIfReady(t); }; this.dispose = () => { this.finished = true; this.disposables.forEach((d) => d.dispose()); this.disposables.clear(); this.values.clear(); this.current = []; }; this.runInner = (t, v) => this.source.f(v).run(this.innerSink(v), (0, scheduler_1.schedulerRelativeTo)(t, this.scheduler)); this.innerSink = (v) => { this.referenceCount++; return { event: (_, a) => { this.values.set(v, a); this.emitIfReady(); }, error: (t, e) => this.error(t, e), end: (t) => { this.referenceCount--; this.endIfReady(t); }, }; }; this.emitIfReady = () => { const values = (0, function_1.pipe)(this.current, RA.map(this.findValue), RA.compact); if (values.length === this.current.length) { this.sink.event(this.scheduler.currentTime(), values); } }; this.endIfReady = (t) => { if (this.finished && this.referenceCount === 0) { this.sink.end(t); this.dispose(); } }; this.findDisposable = (k) => RM.lookup(source.Eq)(k)(this.disposables); this.findValue = (k) => RM.lookup(source.Eq)(k)(this.values); this.findDifference = RA.difference(source.Eq); } } /** * @since 0.9.2 * @category Combinator */ const keyed = (Eq) => (stream) => new Keyed(Eq, stream); exports.keyed = keyed; class Keyed { constructor(Eq, stream) { this.Eq = Eq; this.stream = stream; } run(sink, scheduler) { const s = new KeyedSink(this.Eq, sink, scheduler); return (0, Disposable_1.disposeBoth)(s, this.stream.run(s, scheduler)); } } class KeyedSink { constructor(Eq, sink, scheduler) { this.Eq = Eq; this.sink = sink; this.scheduler = scheduler; this.adapters = new Map(); this.current = []; this.event = (t, values) => { // Clean up after any removed values const removed = (0, function_1.pipe)(this.current, this.findDifference(values)); removed.forEach((r) => { // Send the end signal this.getAdapter(r)[1][0](null); // Delete this.adapters.delete(r); }); this.current = values; // Emit our latest set of streams this.sink.event(t, values.map((a) => (0, function_1.pipe)(a, this.getAdapter, Tuple2_1.fst, Tuple2_1.snd))); values.forEach((a) => (0, function_1.pipe)(a, this.getAdapter, Tuple2_1.fst, Tuple2_1.fst)(a)); }; this.getAdapter = (k) => { return (0, function_1.pipe)(k, this.findAdapter, O.getOrElseW(() => { const endSignal = (0, Adapter_1.create)(); const values = (0, Adapter_1.create)((0, function_1.flow)(M.startWith(k), M.until(endSignal[1]), M.multicast)); const adapter = [values, endSignal]; this.adapters.set(k, adapter); return adapter; })); }; this.error = (t, err) => { this.dispose(); this.sink.error(t, err); }; this.end = (t) => { this.dispose(); this.sink.end(t); }; this.dispose = () => { this.current = []; this.adapters.clear(); }; this.findDifference = RA.difference(Eq); this.findAdapter = (k) => RM.lookup(Eq)(k)(this.adapters); } } /** * @since 0.9.2 * @category Combinator */ const switchFirst = (second) => (first) => (0, function_1.pipe)(first, M.map((a) => (0, function_1.pipe)(second, M.constant(a), M.startWith(a))), M.switchLatest); exports.switchFirst = switchFirst; /** * @since 0.9.2 * @category Combinator */ function mergeFirst(a) { return (b) => (0, function_1.pipe)((0, function_1.pipe)(a, M.constant(O.none)), M.merge((0, function_1.pipe)(b, M.map(O.some))), exports.compact); } exports.mergeFirst = mergeFirst; (0, tslib_1.__exportStar)(require("@most/core"), exports); (0, tslib_1.__exportStar)(require("@most/types"), exports); //# sourceMappingURL=Stream.js.map