import {IO, runIO} from "jabz/io"; import {Monad, monad} from "jabz/monad"; import {Future, fromPromise, sinkFuture} from "./future"; import {Behavior, at} from "./behavior"; import {Stream} from "./stream"; @monad export abstract class Now implements Monad { // Impurely run the now computation abstract run(): A; of(b: B): Now { return new OfNow(b); } static of(b: B): Now { return new OfNow(b); } chain(f: (a: A) => Now): Now { return new ChainNow(this, f); } static multi = false; multi = false; // Definitions below are inserted by Jabz flatten: () => Now; map: (f: (a: A) => B) => Now; mapTo: (b: B) => Now; ap: (a: Now<(a: A) => B>) => Now; lift: (f: Function, ...ms: any[]) => Now; } class OfNow extends Now { constructor(private value: A) { super(); } run() { return this.value; } } class ChainNow extends Now { constructor(private first: Now, private f: (a: any) => Now) { super(); } run() { return this.f(this.first.run()).run(); } } class AsyncNow extends Now> { constructor(private comp: IO) { super(); } run(): Future { return fromPromise(runIO(this.comp)); } } export function async(comp: IO): Now> { return new AsyncNow(comp); } class SampleNow extends Now { constructor(private b: Behavior) { super(); } run(): A { return at(this.b); } } export function sample(b: Behavior): Now { return new SampleNow(b); } class PerformIOStream extends Stream { constructor(s: Stream>) { super(); s.addListener(this); } push(io: IO): void { runIO(io).then((a: A) => this.child.push(a)); } } class PerformStreamNow extends Now> { constructor(private s: Stream>) { super(); } run(): Stream { return new PerformIOStream(this.s); } } export function performStream(s: Stream>): Now> { return new PerformStreamNow(s); } class PerformIOStreamLatest extends Stream { constructor(s: Stream>) { super(); s.addListener(this); } next: number = 0; newest: number = 0; running: number = 0; push(io: IO): void { const time = ++this.next; this.running++; runIO(io).then((a: A) => { this.running--; if (time > this.newest) { if (this.running === 0) { this.next = 0; this.newest = 0; } else { this.newest = time; } this.child.push(a); } }); } } class PerformStreamNowLatest extends Now> { constructor(private s: Stream>) { super(); } run(): Stream { return new PerformIOStreamLatest(this.s); } } export function performStreamLatest(s: Stream>): Now> { return new PerformStreamNowLatest(s); } class PerformIOStreamOrdered extends Stream { constructor(s: Stream>) { super(); s.addListener(this); } nextId: number = 0; next: number = 0; buffer: {value:A}[] = []; // Object-wrapper to support a result as undefined push(io: IO): void { const id = this.nextId++; runIO(io).then((a: A) => { if (id === this.next) { this.buffer[0] = {value: a} this.pushFromBuffer(); } else { this.buffer[id - this.next] = {value: a}; } }); } pushFromBuffer(): void { while (this.buffer[0] !== undefined) { const {value} = this.buffer.shift(); this.child.push(value); this.next++; } } } class PerformStreamNowOrdered extends Now> { constructor(private s: Stream>) { super(); } run(): Stream { return new PerformIOStreamOrdered(this.s); } } export function performStreamOrdered(s: Stream>): Now> { return new PerformStreamNowOrdered(s); } function run(now: Now): A { return now.run(); } class PlanNow extends Now> { constructor(private future: Future>) { super(); } run(): Future { return this.future.map(run); } } export function plan(future: Future>): Now> { return new PlanNow(future); } export function runNow(now: Now>): Promise { return new Promise((resolve, reject) => { now.run().subscribe(resolve); }); }