@typed/fp
Version:
Data Structures and Resources for fp-ts
654 lines • 20.6 kB
JavaScript
"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