git.iko.soy/purescript-stream
on master help sign in
feat(stream): reimplement
commit0813a54fb859022a01dc09fda5c3353e305068de authorTylor Steinberger <tlsteinberger167@gmail.com> dateThu 16 Mar 2017, 13:45 (10 years ago) parenta50a246
44 files changed+2080−2935
.eslintignore−1 ffi/Control/Stream/LinkedList.js−47 ffi/Control/Stream/Stream.js−5 ffi/Control/Stream/combinators/combine.js−97 ffi/Control/Stream/combinators/concat.js−56 ffi/Control/Stream/combinators/delay.js−41 ffi/Control/Stream/combinators/drain.js−4 ffi/Control/Stream/combinators/index.js−8 ffi/Control/Stream/combinators/merge.js−74 ffi/Control/Stream/combinators/mergeConcurrently.js−126 ffi/Control/Stream/combinators/skipRepeatsWith.js−40 ffi/Control/Stream/combinators/switch.js−99 ffi/Control/Stream/disposables/Disposable.js−13 ffi/Control/Stream/disposables/MemoizedDisposable.js−17 ffi/Control/Stream/disposables/SettableDisposable.js−29 ffi/Control/Stream/disposables/disposeSafely.js−3 ffi/Control/Stream/disposables/index.js−12 ffi/Control/Stream/index.js−5 ffi/Control/Stream/multicast/HoldSource.js−45 ffi/Control/Stream/multicast/MulticastDisposable.js−16 ffi/Control/Stream/multicast/MulticastSource.js−62 ffi/Control/Stream/multicast/dispose.js−5 ffi/Control/Stream/multicast/index.js−14 ffi/Control/Stream/runEffects.js−30 ffi/Control/Stream/scheduler/ClockTimer.js−48 ffi/Control/Stream/scheduler/PropagateTask.js−31 ffi/Control/Stream/scheduler/ScheduledTask.js−19 ffi/Control/Stream/scheduler/Scheduler.js−101 ffi/Control/Stream/scheduler/Timeline.js−124 ffi/Control/Stream/scheduler/index.js−16 ffi/Control/Stream/sink/IndexSink.js−24 ffi/Control/Stream/sink/index.js−1 package.json+1−5 rollup.config.js−14 src/Control/Disposable/Disposable.js+32 src/Control/Disposable/Disposable.purs+10 src/Control/Scheduler/Scheduler.js+370 src/Control/Scheduler/Scheduler.purs+32 src/Control/Sink/Sink.purs+9 src/Control/Stream.js−1384 src/Control/Stream.purs+40−319 src/Control/Stream/Stream.js+1244 src/Control/Stream/Stream.purs+339 src/Control/Time.purs+3
.eslintignore−1
@@ -1,2 +1 @@
11output/
2−src/
ffi/Control/Stream/LinkedList.jsdeleted−47
@@ -1,47 +0,0 @@
1−export class LinkedList {
2− constructor () {
3− this.head = null
4− this.length = 0
5− }
6−
7− add (x) {
8− if (this.head !== null) {
9− this.head.prev = x
10− x.next = this.head
11− }
12− this.head = x
13− ++this.length
14− }
15−
16− remove (x) { // eslint-disable-line complexity
17− --this.length
18− if (x === this.head) {
19− this.head = this.head.next
20− }
21− if (x.next !== null) {
22− x.next.prev = x.prev
23− x.next = null
24− }
25− if (x.prev !== null) {
26− x.prev.next = x.next
27− x.prev = null
28− }
29− }
30−
31− isEmpty () {
32− return this.length === 0
33− }
34−
35− dispose () {
36− if (this.isEmpty()) return
37−
38− var x = this.head
39− this.head = null
40− this.length = 0
41−
42− while (x !== null) {
43− x.dispose()
44− x = x.next
45− }
46− }
47−}
ffi/Control/Stream/Stream.jsdeleted−5
@@ -1,5 +0,0 @@
1−export class Stream {
2− constructor (source) {
3− this.source = source
4− }
5−}
ffi/Control/Stream/combinators/combine.jsdeleted−97
@@ -1,97 +0,0 @@
1−/** @license MIT License (c) copyright 2010-2016 original author or authors */
2−/** @author Brian Cavalier */
3−/** @author John Hann */
4−
5−import { map, tail } from '@most/prelude'
6−
7−import { IndexSink } from '../sink/IndexSink'
8−import { Stream } from '../Stream'
9−import { disposeAll } from '../disposables'
10−
11−export const combine2 = f => s1 => s2 => combineArray(f, [s1, s2])
12−export const combine3 = f => s1 => s2 => s3 => combineArray(f, [s1, s2, s3])
13−export const combine4 = f => s1 => s2 => s3 => s4 => combineArray(f, [s1, s2, s3, s4])
14−export const combine5 = f => s1 => s2 => s3 => s4 => s5 => combineArray(f, [s1, s2, s3, s4, s5])
15−
16−function combineArray (f, streams) {
17− return new Stream(combineSources(f, streams))
18−}
19−
20−function combineSources (f, streams) {
21− return new Combine(f, map(getSource, streams))
22−}
23−
24−function getSource (stream) {
25− return stream.source
26−}
27−
28−function Combine (f, sources) {
29− this.f = f
30− this.sources = sources
31−}
32−
33−Combine.prototype.run = function (sink) {
34− return scheduler => {
35− var l = this.sources.length
36− var disposables = new Array(l)
37− var sinks = new Array(l)
38−
39− var mergeSink = new CombineSink(disposables, sinks, sink, this.f)
40−
41− for (var indexSink, i = 0; i < l; ++i) {
42− indexSink = sinks[i] = new IndexSink(i, mergeSink)
43− disposables[i] = this.sources[i].run(indexSink)(scheduler)
44− }
45−
46− return disposeAll(disposables)
47− }
48−}
49−
50−function CombineSink (disposables, sinks, sink, f) {
51− this.sink = sink
52− this.disposables = disposables
53− this.sinks = sinks
54− this.f = f
55−
56− var l = sinks.length
57− this.awaiting = l
58− this.values = new Array(l)
59− this.hasValue = new Array(l)
60−
61− for (var i = 0; i < l; ++i) {
62− this.hasValue[i] = false
63− }
64−
65− this.activeCount = sinks.length
66−}
67−
68−CombineSink.prototype.event = function (t) {
69− return indexedValue => {
70− var i = indexedValue.index
71− var awaiting = this._updateReady(i)
72−
73− this.values[i] = indexedValue.value
74− if (awaiting === 0) {
75− const value = tail(this.values).reduce((f, x) => f(x), this.f(this.values[0]))
76− this.sink.event(t)(value)
77− }
78− }
79−}
80−
81−CombineSink.prototype._updateReady = function (index) {
82− if (this.awaiting > 0) {
83− if (!this.hasValue[index]) {
84− this.hasValue[index] = true
85− this.awaiting -= 1
86− }
87− }
88− return this.awaiting
89−}
90−
91−CombineSink.prototype.end = function (t, indexedValue) {
92− this.disposables[indexedValue.index].dispose()
93−
94− if (--this.activeCount === 0) {
95− this.sink.end(t)
96− }
97−}
ffi/Control/Stream/combinators/concat.jsdeleted−56
@@ -1,56 +0,0 @@
1−import { MemoizedDisposable } from '../disposables/MemoizedDisposable'
2−import { Stream } from '../Stream'
3−
4−export const concat = s1 => s2 => continueWith(() => s2)(s1)
5−
6−export const continueWith = f => stream => new Stream(new ContinueWith(f, stream.source))
7−
8−function ContinueWith (f, source) {
9− this.f = f
10− this.source = source
11−}
12−
13−ContinueWith.prototype.run = function (sink) {
14− return scheduler => {
15− return new ContinueWithSink(this.f, this.source, sink, scheduler)
16− }
17−}
18−
19−function ContinueWithSink (f, source, sink, scheduler) {
20− this.f = f
21− this.sink = sink
22− this.scheduler = scheduler
23− this.active = true
24− this.disposable = new MemoizedDisposable(source.run(this)(scheduler))
25−}
26−
27−ContinueWithSink.prototype.event = function (t) {
28− return x => {
29− if (!this.active) {
30− return
31− }
32− this.sink.event(t)(x)
33− }
34−}
35−
36−ContinueWithSink.prototype.end = function (t, x) {
37− if (!this.active) {
38− return
39− }
40−
41− this.disposable.dispose()
42− this._startNext(t, x, this.sink)
43−}
44−
45−ContinueWithSink.prototype._startNext = function (t, x, sink) {
46− this._continue(this.f, x, sink)
47−}
48−
49−ContinueWithSink.prototype._continue = function (f, x, sink) {
50− return f(x).source.run(sink)(this.scheduler)
51−}
52−
53−ContinueWithSink.prototype.dispose = function () {
54− this.active = false
55− return this.disposable.dispose()
56−}
ffi/Control/Stream/combinators/delay.jsdeleted−41
@@ -1,41 +0,0 @@
1−import { endTask, eventTask } from '../scheduler'
2−
3−import { Stream } from '../Stream'
4−import { disposeAll } from '../disposables'
5−
6−export const delay = delayTime => stream =>
7− delayTime <= 0 ? stream : new Stream(new Delay(delayTime, stream.source))
8−
9−class Delay {
10− constructor (dt, source) {
11− this.dt = dt
12− this.source = source
13− }
14−
15− run (sink) {
16− return scheduler => {
17− const delaySink = new DelaySink(this.dt, sink, scheduler)
18− return disposeAll([delaySink, this.source.run(delaySink)(scheduler)])
19− }
20− }
21−}
22−
23−class DelaySink {
24− constructor (dt, sink, scheduler) {
25− this.dt = dt
26− this.sink = sink
27− this.scheduler = scheduler
28− }
29−
30− dispose () {
31− this.scheduler.cancelAll(task => task.sink === this.sink)
32− }
33−
34− event (t) {
35− return x => this.scheduler.delay(this.dt)(eventTask(x)(this.sink))
36− }
37−
38− end (t) {
39− this.scheduler.delay(this.dt)(endTask(this.sink))
40− }
41−}
ffi/Control/Stream/combinators/drain.jsdeleted−4
@@ -1,4 +0,0 @@
1−import { defaultScheduler } from '../scheduler'
2−import { runEffects } from '../runEffects'
3−
4−export const drain = stream => () => runEffects(stream, defaultScheduler)
ffi/Control/Stream/combinators/index.jsdeleted−8
@@ -1,8 +0,0 @@
1−export * from './drain'
2−export * from './mergeConcurrently'
3−export * from './combine'
4−export * from './merge'
5−export * from './concat'
6−export * from './switch'
7−export * from './skipRepeatsWith'
8−export * from './delay'
ffi/Control/Stream/combinators/merge.jsdeleted−74
@@ -1,74 +0,0 @@
1−/** @license MIT License (c) copyright 2010-2016 original author or authors */
2−/** @author Brian Cavalier */
3−/** @author John Hann */
4−
5−import { IndexSink } from '../sink/IndexSink'
6−import { Stream } from '../Stream'
7−import { disposeAll } from '../disposables'
8−import { reduce } from '@most/prelude'
9−
10−/**
11− * @param {Array} streams array of stream to merge
12− * @returns {Stream} stream containing events from all input observables
13− * in time order. If two events are simultaneous they will be merged in
14− * arbitrary order.
15− */
16−export function merge (streams) {
17− return new Stream(mergeSources(streams))
18−}
19−
20−/**
21− * This implements fusion/flattening for merge. It will
22− * fuse adjacent merge operations. For example:
23− * - a.merge(b).merge(c) effectively becomes merge(a, b, c)
24− * - merge(a, merge(b, c)) effectively becomes merge(a, b, c)
25− * It does this by concatenating the sources arrays of
26− * any nested Merge sources, in effect "flattening" nested
27− * merge operations into a single merge.
28− */
29−function mergeSources (streams) {
30− return new Merge(reduce(appendSources, [], streams))
31−}
32−
33−function appendSources (sources, stream) {
34− var source = stream.source
35− return source instanceof Merge
36− ? sources.concat(source.sources)
37− : sources.concat(source)
38−}
39−
40−function Merge (sources) {
41− this.sources = sources
42−}
43−
44−Merge.prototype.run = function (sink, scheduler) {
45− var l = this.sources.length
46− var disposables = new Array(l)
47− var sinks = new Array(l)
48−
49− var mergeSink = new MergeSink(disposables, sinks, sink)
50−
51− for (var indexSink, i = 0; i < l; ++i) {
52− indexSink = sinks[i] = new IndexSink(i, mergeSink)
53− disposables[i] = this.sources[i].run(indexSink, scheduler)
54− }
55−
56− return disposeAll(disposables)
57−}
58−
59−function MergeSink (disposables, sinks, sink) {
60− this.sink = sink
61− this.disposables = disposables
62− this.activeCount = sinks.length
63−}
64−
65−MergeSink.prototype.event = function (t, indexValue) {
66− this.sink.event(t, indexValue.value)
67−}
68−
69−MergeSink.prototype.end = function (t, indexedValue) {
70− dispose.tryDispose(t, this.disposables[indexedValue.index], this.sink)
71− if (--this.activeCount === 0) {
72− this.sink.end(t, indexedValue.value)
73− }
74−}
ffi/Control/Stream/combinators/mergeConcurrently.jsdeleted−126
@@ -1,126 +0,0 @@
1−/** @license MIT License (c) copyright 2010-2016 original author or authors */
2−/** @author Brian Cavalier */
3−/** @author John Hann */
4−
5−import { curry2, curry3, id as identity } from '@most/prelude'
6−
7−import { LinkedList } from '../LinkedList'
8−import { MemoizedDisposable } from '../disposables/MemoizedDisposable'
9−import { Stream } from '../Stream'
10−
11−export const flatMap =
12− curry2((stream, f) => mergeMapConcurrently(f, Infinity, stream))
13−
14−export const mergeConcurrently = curry2((concurrency, stream) =>
15− mergeMapConcurrently(identity, concurrency, stream))
16−
17−export const join =
18− mergeConcurrently(Infinity)
19−
20−export const mergeMapConcurrently = curry3((f, concurrency, stream) =>
21− new Stream(new MergeConcurrently(f, concurrency, stream.source)))
22−
23−class MergeConcurrently {
24− constructor (f, concurrency, source) {
25− this.f = f
26− this.concurrency = concurrency
27− this.source = source
28− }
29−
30− run (sink) {
31− return scheduler => new Outer(this.f, this.concurrency, this.source, sink, scheduler)
32− }
33−}
34−
35−class Outer {
36− constructor (f, concurrency, source, sink, scheduler) {
37− this.f = f
38− this.concurrency = concurrency
39− this.sink = sink
40− this.scheduler = scheduler
41− this.pending = []
42− this.current = new LinkedList()
43− this.disposable = new MemoizedDisposable(source.run(this)(scheduler))
44− this.active = true
45− }
46−
47− event (t) {
48− return x => this._addInner(t, x)
49− }
50−
51− _addInner (t, x) {
52− if (this.current.length < this.concurrency) {
53− this._startInner(t, x)
54− } else {
55− this.pending.push(x)
56− }
57− }
58−
59− _startInner (t, x) {
60− try {
61− this._initInner(t, x)
62− } catch (e) {
63− this.error(t, e)
64− }
65− }
66−
67− _initInner (t, x) {
68− const innerSink = new Inner(t, this, this.sink)
69− innerSink.disposable = mapAndRun(this.f, x, innerSink, this.scheduler)
70− this.current.add(innerSink)
71− }
72−
73− end (t) {
74− this.active = false
75− this.disposable.dispose()
76− this._checkEnd(t)
77− }
78−
79− dispose () {
80− this.active = false
81− this.pending.length = 0
82− this.disposable.dispose()
83− this.current.dispose()
84− }
85−
86− _endInner (t, inner) {
87− this.current.remove(inner)
88− inner.dispose()
89−
90− if (this.pending.length === 0) {
91− this._checkEnd(t)
92− } else {
93− this._startInner(t, this.pending.shift())
94− }
95− }
96−
97− _checkEnd (t) {
98− if (!this.active && this.current.isEmpty()) {
99− this.sink.end(t)
100− }
101− }
102−}
103−
104−const mapAndRun = (f, x, sink, scheduler) => f(x).source.run(sink)(scheduler)
105−
106−class Inner {
107− constructor (time, outer, sink) {
108− this.prev = this.next = null
109− this.time = time
110− this.outer = outer
111− this.sink = sink
112− this.disposable = void 0
113− }
114−
115− event (t) {
116− return x => this.sink.event(Math.max(t, this.time))(x)
117− }
118−
119− end (t) {
120− this.outer._endInner(Math.max(t, this.time), this)
121− }
122−
123− dispose () {
124− return this.disposable.dispose()
125− }
126−}
ffi/Control/Stream/combinators/skipRepeatsWith.jsdeleted−40
@@ -1,40 +0,0 @@
1−import { Stream } from '../Stream'
2−
3−export const skipRepeatsWith = equals => stream =>
4− new Stream(new SkipRepeats(equals, stream))
5−
6−class SkipRepeats {
7− constructor (equals, source) {
8− this.equals = equals
9− this.source = source
10− }
11−
12− run (sink) {
13− return scheduler => this.source.run(new SkipRepeatsSink(this.equals, sink), scheduler)
14− }
15−}
16−
17−class SkipRepeatsSink {
18− constructor (equals, sink) {
19− this.equals = equals
20− this.value = void 0
21− this.init = true
22− }
23−
24− event (t) {
25− return x => {
26− if (this.init) {
27− this.init = false
28− this.value = x
29− this.sink.event(t, x)
30− } else if (!this.equals(this.value)(x)) {
31− this.value = x
32− this.sink.event(t)(x)
33− }
34− }
35− }
36−
37− end (t) {
38− this.sink.end(t)
39− }
40−}
ffi/Control/Stream/combinators/switch.jsdeleted−99
@@ -1,99 +0,0 @@
1−import { disposeAll, emptyDisposable } from '../disposables'
2−
3−import { Stream } from '../Stream'
4−
5−/**
6− * Given a stream of streams, return a new stream that adopts the behavior
7− * of the most recent inner stream.
8− * @param {Stream} stream of streams on which to switch
9− * @returns {Stream} switching stream
10− */
11−function switchLatest (stream) {
12− return new Stream(new Switch(stream.source))
13−}
14−
15−export { switchLatest as switch }
16−
17−function Switch (source) {
18− this.source = source
19−}
20−
21−Switch.prototype.run = function (sink) {
22− return scheduler => {
23− const switchSink = new SwitchSink(sink, scheduler)
24− return disposeAll([switchSink, this.source.run(switchSink)(scheduler)])
25− }
26−}
27−
28−function SwitchSink (sink, scheduler) {
29− this.sink = sink
30− this.scheduler = scheduler
31− this.current = null
32− this.ended = false
33−}
34−
35−SwitchSink.prototype.event = function (t) {
36− return stream => {
37− this._disposeCurrent(t) // TODO: capture the result of this dispose
38− this.current = new Segment(t, Infinity, this, this.sink)
39− this.current.disposable = stream.source.run(this.current)(this.scheduler)
40− }
41−}
42−
43−SwitchSink.prototype.end = function (t, x) {
44− this.ended = true
45− this._checkEnd(t, x)
46−}
47−
48−SwitchSink.prototype.dispose = function () {
49− return this._disposeCurrent(this.scheduler.now())
50−}
51−
52−SwitchSink.prototype._disposeCurrent = function (t) {
53− if (this.current !== null) {
54− return this.current._dispose(t)
55− }
56−}
57−
58−SwitchSink.prototype._disposeInner = function (t, inner) {
59− inner._dispose(t) // TODO: capture the result of this dispose
60− if (inner === this.current) {
61− this.current = null
62− }
63−}
64−
65−SwitchSink.prototype._checkEnd = function (t, x) {
66− if (this.ended && this.current === null) {
67− this.sink.end(t, x)
68− }
69−}
70−
71−SwitchSink.prototype._endInner = function (t, x, inner) {
72− this._disposeInner(t, inner)
73− this._checkEnd(t, x)
74−}
75−
76−function Segment (min, max, outer, sink) {
77− this.min = min
78− this.max = max
79− this.outer = outer
80− this.sink = sink
81− this.disposable = emptyDisposable
82−}
83−
84−Segment.prototype.event = function (t) {
85− return x => {
86− if (t < this.max) {
87− this.sink.event(Math.max(t, this.min))(x)
88− }
89− }
90−}
91−
92−Segment.prototype.end = function (t, x) {
93− this.outer._endInner(Math.max(t, this.min), x, this)
94−}
95−
96−Segment.prototype._dispose = function (t) {
97− this.max = t
98− this.disposable.dispose()
99−}
ffi/Control/Stream/disposables/Disposable.jsdeleted−13
@@ -1,13 +0,0 @@
1−export class Disposable {
2− /**
3− * Create a new Disposable which will dispose its underlying resource.
4− * @param {function} dispose function
5− * @param {*?} data any data to be passed to disposer function
6− */
7− constructor (dispose, data) {
8− this._dispose = dispose
9− this._data = data
10−
11− this.dispose = () => dispose(data)
12− }
13−}
ffi/Control/Stream/disposables/MemoizedDisposable.jsdeleted−17
@@ -1,17 +0,0 @@
1−export class MemoizedDisposable {
2− constructor (disposable) {
3− this.disposed = false
4− this.value = undefined
5− this.disposable = disposable
6− }
7−
8− dispose () {
9− if (!this.disposed) {
10− this.disposed = true
11− this.value = this.disposable.dispose()
12− this.disposable = undefined
13− }
14−
15− return this.value
16− }
17−}
ffi/Control/Stream/disposables/SettableDisposable.jsdeleted−29
@@ -1,29 +0,0 @@
1−export class SettableDisposable {
2− constructor () {
3− this.disposable = void 0
4− this.disposed = false
5− this.result = void 0
6− }
7−
8− setDisposable (disposable) {
9− this.disposable = disposable
10−
11− if (this.disposed) {
12− this.result = disposable.dispose()
13− }
14− }
15−
16− dispose () {
17− if (this.disposed) {
18− return this.result
19− }
20−
21− this.disposed = true
22−
23− if (this.disposable !== void 0) {
24− this.result = this.disposable.dispose()
25− }
26−
27− return this.result
28− }
29−}
ffi/Control/Stream/disposables/disposeSafely.jsdeleted−3
@@ -1,3 +0,0 @@
1−export function disposeSafely (disposable) {
2− disposable.dispose()
3−}
ffi/Control/Stream/disposables/index.jsdeleted−12
@@ -1,12 +0,0 @@
1−import { curry2, id, map } from '@most/prelude'
2−
3−import { Disposable } from './Disposable'
4−import { MemoizedDisposable } from './MemoizedDisposable'
5−import { disposeSafely } from './disposeSafely'
6−
7−export const createDisposable = curry2((dispose, data) => new MemoizedDisposable(new Disposable(dispose, data)))
8−
9−export const emptyDisposable = new Disposable(id, undefined)
10−
11−export const disposeAll = disposables =>
12− createDisposable(curry2(map)(disposeSafely), disposables)
ffi/Control/Stream/index.jsdeleted−5
@@ -1,5 +0,0 @@
1−/* @flow */
2−export * from './disposables'
3−export * from './scheduler'
4−export * from './combinators'
5−export * from './multicast'
ffi/Control/Stream/multicast/HoldSource.jsdeleted−45
@@ -1,45 +0,0 @@
1−import { append, drop } from '@most/prelude'
2−
3−import { MulticastSource } from './MulticastSource'
4−
5−export class HoldSource extends MulticastSource {
6− constructor (source, bufferSize) {
7− super(source)
8− this.bufferSize = bufferSize
9− this.has = false
10− this.buffer = []
11− }
12−
13− add (sink, scheduler) {
14− if (this.has) {
15− pushEvents(this.buffer, sink, scheduler)
16− }
17−
18− return super.add(sink, scheduler)
19− }
20−
21− event (time) {
22− return value => {
23− this.has = true
24− this.buffer = dropAndAppend(value, this.buffer, this.bufferSize)
25−
26− return super.event(time)(value)
27− }
28− }
29−}
30−
31−function pushEvents (buffer, sink, scheduler) {
32− const length = buffer.length
33−
34− for (let i = 0; i < length; ++i) {
35− sink.event(scheduler.now(), buffer[i])
36− }
37−}
38−
39−export function dropAndAppend (value, buffer, bufferSize) {
40− if (buffer.length === bufferSize) {
41− return append(value, drop(1, buffer))
42− }
43−
44− return append(value, buffer)
45−}
ffi/Control/Stream/multicast/MulticastDisposable.jsdeleted−16
@@ -1,16 +0,0 @@
1−export class MulticastDisposable {
2− constructor (source, sink) {
3− this.source = source
4− this.sink = sink
5− this.disposed = false
6− }
7−
8− dispose () {
9− if (this.disposed) {
10− return
11− }
12− this.disposed = true
13− const remaining = this.source.remove(this.sink)
14− return remaining === 0 && this.source._dispose()
15− }
16−}
ffi/Control/Stream/multicast/MulticastSource.jsdeleted−62
@@ -1,62 +0,0 @@
1−import { append, findIndex, remove } from '@most/prelude'
2−import { dispose, emptyDisposable } from './dispose'
3−
4−import { MulticastDisposable } from './MulticastDisposable'
5−
6−export class MulticastSource {
7− constructor (source) {
8− this.source = source
9− this.sinks = []
10− this._disposable = emptyDisposable
11− }
12−
13− run (sink) {
14− return scheduler => {
15− const n = this.add(sink, scheduler)
16− if (n === 1) {
17− this._disposable = this.source.run(this)(scheduler)
18− }
19− return new MulticastDisposable(this, sink)
20− }
21− }
22−
23− _dispose () {
24− const disposable = this._disposable
25− this._disposable = emptyDisposable
26− return Promise.resolve(disposable).then(dispose)
27− }
28−
29− add (sink) {
30− this.sinks = append(sink, this.sinks)
31− return this.sinks.length
32− }
33−
34− remove (sink) {
35− const i = findIndex(sink, this.sinks)
36− // istanbul ignore next
37− if (i >= 0) {
38− this.sinks = remove(i, this.sinks)
39− }
40−
41− return this.sinks.length
42− }
43−
44− event (time) {
45− return value => {
46− const s = this.sinks
47− if (s.length === 1) {
48− return s[0].event(time)(value)
49− }
50− for (let i = 0; i < s.length; ++i) {
51− s[i].event(time)(value)
52− }
53− }
54− }
55−
56− end (time) {
57− const s = this.sinks
58− for (let i = 0; i < s.length; ++i) {
59− s[i].end(time)
60− }
61− }
62−}
ffi/Control/Stream/multicast/dispose.jsdeleted−5
@@ -1,5 +0,0 @@
1−export const dispose = disposable => disposable.dispose()
2−
3−export const emptyDisposable = {
4− dispose () {}
5−}
ffi/Control/Stream/multicast/index.jsdeleted−14
@@ -1,14 +0,0 @@
1−import { HoldSource } from './HoldSource'
2−import { MulticastSource } from './MulticastSource'
3−import { Stream } from '../Stream'
4−
5−export function multicast (stream) {
6− const source = stream.source
7−
8− return source instanceof MulticastSource
9− ? stream
10− : new Stream(new MulticastSource(source))
11−}
12−
13−export const hold = bufferSize => stream =>
14− new Stream(new HoldSource(stream.source, bufferSize))
ffi/Control/Stream/runEffects.jsdeleted−30
@@ -1,30 +0,0 @@
1−import { SettableDisposable } from './disposables/SettableDisposable'
2−
3−export function runEffects (stream, scheduler) {
4− return runSourceEffects(stream.source, scheduler)
5−}
6−
7−const runSourceEffects = (source, scheduler) => runSource(source, scheduler)
8−
9−function runSource (source, scheduler) {
10− const disposable = new SettableDisposable()
11− const observer = new RunEffectsSink(disposable)
12−
13− disposable.setDisposable(source.run(observer)(scheduler))
14−
15− return {}
16−}
17−
18−var RunEffectsSink = function RunEffectsSink (disposable) {
19− this._disposable = disposable
20− this.active = true
21−}
22−
23−RunEffectsSink.prototype.event = function event (t) {
24− return function (x) {}
25−}
26−
27−RunEffectsSink.prototype.end = function end (t) {
28− this.active = false
29− Promise.resolve(this._disposable).then(d => d.dispose())
30−}
ffi/Control/Stream/scheduler/ClockTimer.jsdeleted−48
@@ -1,48 +0,0 @@
1−/** @license MIT License (c) copyright 2010-2016 original author or authors */
2−/** @author Brian Cavalier */
3−/** @author John Hann */
4−
5−/*global setTimeout, clearTimeout*/
6−
7−export function defer (task) {
8− return Promise.resolve(task).then(runTask)
9−}
10−
11−export function runTask (task) {
12− return task.run()
13−}
14−
15−export class ClockTimer {
16− constructor () {
17− this.now = Date.now
18− }
19−
20− setTimer (f, dt) {
21− return dt <= 0 ? runAsap(f) : setTimeout(f, dt)
22− }
23−
24− clearTimer (t) {
25− return t instanceof Asap ? t.cancel() : clearTimeout(t)
26− }
27−}
28−
29−class Asap {
30− constructor (f) {
31− this.f = f
32− this.active = true
33− }
34−
35− run () {
36− return this.active && this.f()
37− }
38−
39− cancel () {
40− this.active = false
41− }
42−}
43−
44−function runAsap (f) {
45− const task = new Asap(f)
46− defer(task)
47− return task
48−}
ffi/Control/Stream/scheduler/PropagateTask.jsdeleted−31
@@ -1,31 +0,0 @@
1−import { curry2, curry3 } from '@most/prelude'
2−
3−const propagateTask =
4− curry3((run, value, sink) => new PropagateTask(run, value, sink))
5−
6−export const eventTask =
7− curry2((value, sink) => propagateTask(runEvent, value, sink))
8−
9−export const endTask = sink => propagateTask(runEnd, void 0, sink)
10−
11−class PropagateTask {
12− constructor (run, value, sink) {
13− let active = true
14− this.sink = sink
15−
16− this.dispose = () => { active = false }
17− this.run = t => {
18− if (!active) return
19−
20− run(t, value, sink)
21− }
22− }
23−}
24−
25−function runEvent (time, value, sink) {
26− sink.event(time)(value)
27−}
28−
29−function runEnd (time, value, sink) {
30− sink.end(time, value)
31−}
ffi/Control/Stream/scheduler/ScheduledTask.jsdeleted−19
@@ -1,19 +0,0 @@
1−/* @flow */
2−export class ScheduledTask {
3− constructor (delay, period, task, scheduler) {
4− this.time = delay
5− this.period = period
6− this.task = task
7− this.scheduler = scheduler
8− this.active = true
9− }
10−
11− run () {
12− this.task.run(this.time)
13− }
14−
15− dispose () {
16− this.scheduler.cancel(this)
17− this.task.dispose()
18− }
19−}
ffi/Control/Stream/scheduler/Scheduler.jsdeleted−101
@@ -1,101 +0,0 @@
1−/* @flow */
2−import { ScheduledTask } from './ScheduledTask'
3−
4−function runTask (task) {
5− try {
6− return task.run()
7− } catch (e) {
8− console.error(e)
9− }
10−}
11−
12−export class Scheduler {
13− constructor (timer, timeline) {
14− this.timer = timer
15− this.timeline = timeline
16−
17− this._timer = null
18− this._nextArrival = Infinity
19−
20− this._runReadyTasksBound = () => this._runReadyTasks(this.now())
21− }
22−
23− now () {
24− return this.timer.now()
25− }
26−
27− asap (task) {
28− return this.schedule(0)(-1)(task)
29− }
30−
31− delay (delay) {
32− return task => this.schedule(delay)(-1)(task)
33− }
34−
35− periodic (period) {
36− return task => this.schedule(0)(period)(task)
37− }
38−
39− schedule (delay) {
40− return period => task => {
41− const now = this.now()
42− const st = new ScheduledTask(now + Math.max(0, delay), period, task, this)
43−
44− this.timeline.add(st)
45− this._scheduleNextRun(now)
46− return st
47− }
48− }
49−
50− cancel (task) {
51− task.active = false
52− if (this.timeline.remove(task)) {
53− this._reschedule()
54− }
55− }
56−
57− cancelAll (f) {
58− this.timeline.removeAll(f)
59− this._reschedule()
60− }
61−
62− _reschedule () {
63− if (this.timeline.isEmpty()) {
64− this._unschedule()
65− } else {
66− this._scheduleNextRun(this.now())
67− }
68− }
69−
70− _unschedule () {
71− this.timer.clearTimer(this._timer)
72− this._timer = null
73− }
74−
75− _scheduleNextRun (now) { // eslint-disable-line complexity
76− if (this.timeline.isEmpty()) {
77− return
78− }
79−
80− const nextArrival = this.timeline.nextArrival()
81−
82− if (this._timer === null) {
83− this._scheduleNextArrival(nextArrival, now)
84− } else if (nextArrival < this._nextArrival) {
85− this._unschedule()
86− this._scheduleNextArrival(nextArrival, now)
87− }
88− }
89−
90− _scheduleNextArrival (nextArrival, now) {
91− this._nextArrival = nextArrival
92− const delay = Math.max(0, nextArrival - now)
93− this._timer = this.timer.setTimer(this._runReadyTasksBound, delay)
94− }
95−
96− _runReadyTasks (now) {
97− this._timer = null
98− this.timeline.runTasks(now, runTask)
99− this._scheduleNextRun(this.now())
100− }
101−}
ffi/Control/Stream/scheduler/Timeline.jsdeleted−124
@@ -1,124 +0,0 @@
1−/** @license MIT License (c) copyright 2010-2016 original author or authors */
2−/** @author Brian Cavalier */
3−/** @author John Hann */
4−
5−import * as base from '@most/prelude'
6−
7−export class Timeline {
8− constructor () {
9− this.tasks = []
10− }
11−
12− nextArrival () {
13− return this.isEmpty() ? Infinity : this.tasks[0].time
14− }
15−
16− isEmpty () {
17− return this.tasks.length === 0
18− }
19−
20− add (st) {
21− insertByTime(st, this.tasks)
22− }
23−
24− remove (st) {
25− const i = binarySearch(st.time, this.tasks)
26−
27− if (i >= 0 && i < this.tasks.length) {
28− const at = base.findIndex(st, this.tasks[i].events)
29− if (at >= 0) {
30− this.tasks[i].events.splice(at, 1)
31− return true
32− }
33− }
34−
35− return false
36− }
37−
38− removeAll (f) {
39− for (let i = 0; i < this.tasks.length; ++i) {
40− removeAllFrom(f, this.tasks[i])
41− }
42− }
43−
44− runTasks (t, runTask) {
45− const tasks = this.tasks
46− const l = tasks.length
47− let i = 0
48−
49− while (i < l && tasks[i].time <= t) {
50− ++i
51− }
52−
53− this.tasks = tasks.slice(i)
54−
55− // Run all ready tasks
56− for (let j = 0; j < i; ++j) {
57− this.tasks = runReadyTasks(runTask, tasks[j].events, this.tasks)
58− }
59− }
60−}
61−
62−function runReadyTasks (runTask, events, tasks) { // eslint-disable-line complexity
63− for (let i = 0; i < events.length; ++i) {
64− const task = events[i]
65−
66− if (task.active) {
67− runTask(task)
68−
69− // Reschedule periodic repeating tasks
70− // Check active again, since a task may have canceled itself
71− if (task.period >= 0 && task.active) {
72− task.time = task.time + task.period
73− insertByTime(task, tasks)
74− }
75− }
76− }
77−
78− return tasks
79−}
80−
81−function insertByTime (task, timeslots) { // eslint-disable-line complexity
82− const l = timeslots.length
83−
84− if (l === 0) {
85− timeslots.push(newTimeslot(task.time, [task]))
86− return
87− }
88−
89− const i = binarySearch(task.time, timeslots)
90−
91− if (i >= l) {
92− timeslots.push(newTimeslot(task.time, [task]))
93− } else if (task.time === timeslots[i].time) {
94− timeslots[i].events.push(task)
95− } else {
96− timeslots.splice(i, 0, newTimeslot(task.time, [task]))
97− }
98−}
99−
100−function removeAllFrom (f, timeslot) {
101− timeslot.events = base.removeAll(f, timeslot.events)
102−}
103−
104−function binarySearch (t, sortedArray) { // eslint-disable-line complexity
105− let lo = 0
106− let hi = sortedArray.length
107− let mid, y
108−
109− while (lo < hi) {
110− mid = Math.floor((lo + hi) / 2)
111− y = sortedArray[mid]
112−
113− if (t === y.time) {
114− return mid
115− } else if (t < y.time) {
116− hi = mid
117− } else {
118− lo = mid + 1
119− }
120− }
121− return hi
122−}
123−
124−const newTimeslot = (t, events) => ({ time: t, events: events })
ffi/Control/Stream/scheduler/index.jsdeleted−16
@@ -1,16 +0,0 @@
1−import { ClockTimer } from './ClockTimer'
2−import { Scheduler } from './Scheduler'
3−import { Timeline } from './Timeline'
4−import { curry2 } from '@most/prelude'
5−
6−export { endTask, eventTask } from './PropagateTask'
7−
8−export const defaultScheduler = new Scheduler(new ClockTimer(), new Timeline())
9−
10−export const scheduleTasks = curry2((tasks, scheduler) => {
11− const scheduledTasks = tasks.map(task => scheduler.asap(task))
12−
13− return {
14− dispose: () => scheduledTasks.forEach(task => task.dispose())
15− }
16−})
ffi/Control/Stream/sink/IndexSink.jsdeleted−24
@@ -1,24 +0,0 @@
1−export class IndexSink {
2− constructor (i, sink) {
3− this.sink = sink
4− this.index = i
5− this.active = true
6− this.value = void 0
7− }
8−
9− event (t) {
10− return x => {
11− if (!this.active) return
12−
13− this.value = x
14− this.sink.event(t)(this)
15− }
16− }
17−
18− end (t) {
19− if (!this.active) return
20−
21− this.active = false
22− this.sink.end(t, { index: this.index })
23− }
24−}
ffi/Control/Stream/sink/index.jsdeleted−1
@@ -1 +0,0 @@
1−export * from './IndexSink'
package.json+1−5
@@ -5,7 +5,7 @@
55 "main": "index.js",
66 "scripts": {
77 "start": "yarn build && pulp run",
8− "build": "rollup -c && pulp build",
8+ "build": "pulp build",
99 "changelog": "conventional-changelog -i CHANGELOG.md -s -r 0 -p angular",
1010 "clean": "rimraf lib lib.es2015",
1111 "commit": "git-cz",
@@ -32,10 +32,6 @@
3232 "husky": "^0.13.2",
3333 "pulp": "^10.0.4",
3434 "purescript": "^0.10.7",
35− "rollup": "^0.41.4",
36− "rollup-plugin-buble": "^0.15.0",
37− "rollup-plugin-flow": "^1.1.1",
38− "rollup-plugin-node-resolve": "^2.0.0",
3935 "uglify-js": "^2.8.11",
4036 "validate-commit-msg": "^2.11.2"
4137 },
rollup.config.jsdeleted−14
@@ -1,14 +0,0 @@
1−import buble from 'rollup-plugin-buble'
2−import flow from 'rollup-plugin-flow'
3−import resolve from 'rollup-plugin-node-resolve'
4−
5−export default {
6− entry: 'ffi/Control/Stream/index.js',
7− dest: 'src/Control/Stream.js',
8− format: 'cjs',
9− plugins: [
10− flow(),
11− buble(),
12− resolve({ module: true, jsnext: true })
13− ]
14−}
src/Control/Disposable/Disposable.jsnew file+32
@@ -0,0 +1,32 @@
1+exports.emptyDisposable = { dispose: function () { return {} } }
2+
3+exports.disposeAll = function (disposables) {
4+ var disposed = false
5+
6+ function dispose () {
7+ if (disposed) return
8+
9+ disposables.forEach(function (disposable) {
10+ disposable.dispose()
11+ })
12+
13+ disposed = true
14+ }
15+
16+ return { dispose: dispose }
17+}
18+
19+exports.lazyDisposable = function (f) {
20+ var disposed = false
21+
22+ function dispose () {
23+ if (disposed) return
24+ var disposable = f()
25+
26+ disposable.dispose()
27+
28+ disposed = true
29+ }
30+
31+ return { dispose: dispose }
32+}
src/Control/Disposable/Disposable.pursnew file+10
@@ -0,0 +1,10 @@
1+module Control.Stream.Disposable where
2+
3+import Control.Monad.Eff (Pure)
4+import Data.Unit (Unit)
5+
6+type Disposable = { dispose :: Pure Unit }
7+
8+foreign import emptyDisposable :: Disposable
9+foreign import disposeAll :: Array Disposable -> Disposable
10+foreign import lazyDisposable :: (Unit -> Disposable) -> Disposable
src/Control/Scheduler/Scheduler.jsnew file+370
@@ -0,0 +1,370 @@
1+exports.scheduleTasks = function (f) {
2+ return function (tasks) {
3+ var scheduledTasks = tasks.map(f.bind(scheduler))
4+
5+ function dispose () {
6+ scheduledTasks.forEach(function (task) {
7+ task.dispose()
8+ })
9+ }
10+
11+ return { dispose: dispose }
12+ }
13+}
14+
15+exports.eventTask = function (sink) {
16+ return function (value) {
17+ return new PropagateTask(runEvent, value, sink)
18+ }
19+}
20+
21+exports.endTask = function (sink) {
22+ return new PropagateTask(runEnd, void 0, sink)
23+}
24+
25+function PropagateTask (run, value, sink) {
26+ this.active = true
27+ this._run = run
28+ this.value = value
29+ this.sink = sink
30+}
31+
32+PropagateTask.prototype.dispose = function () {
33+ this.active = false
34+}
35+
36+PropagateTask.prototype.run = function (time) {
37+ if (!this.active) return
38+
39+ this._run(time, this.value, this.sink)
40+}
41+
42+function runEvent (time, value, sink) {
43+ sink.event(time)(value)
44+}
45+
46+function runEnd (time, value, sink) {
47+ sink.end(time)
48+}
49+
50+function defer (task) {
51+ return Promise.resolve(task).then(runTask)
52+}
53+
54+function runTask (task) {
55+ return task.run()
56+}
57+
58+var ClockTimer = function ClockTimer () {
59+ this.now = Date.now
60+}
61+
62+ClockTimer.prototype.setTimer = function setTimer (f, dt) {
63+ return dt <= 0 ? runAsap(f) : setTimeout(f, dt)
64+}
65+
66+ClockTimer.prototype.clearTimer = function clearTimer (t) {
67+ return t instanceof Asap ? t.cancel() : clearTimeout(t)
68+}
69+
70+var Asap = function Asap (f) {
71+ this.f = f
72+ this.active = true
73+}
74+
75+Asap.prototype.run = function run () {
76+ return this.active && this.f()
77+}
78+
79+Asap.prototype.cancel = function cancel () {
80+ this.active = false
81+}
82+
83+function runAsap (f) {
84+ var task = new Asap(f)
85+ defer(task)
86+ return task
87+}
88+
89+var ScheduledTask = function ScheduledTask (delay, period, task, scheduler) {
90+ this.time = delay
91+ this.period = period
92+ this.task = task
93+ this.scheduler = scheduler
94+ this.active = true
95+}
96+
97+ScheduledTask.prototype.run = function run () {
98+ this.task.run(this.time)
99+}
100+
101+ScheduledTask.prototype.dispose = function dispose () {
102+ this.scheduler.cancel(this)
103+ this.task.dispose()
104+}
105+
106+function runTask$1 (task) {
107+ try {
108+ return task.run()
109+ } catch (e) {
110+ console.error(e)
111+ }
112+}
113+
114+var Scheduler = function Scheduler (timer, timeline) {
115+ var this$1 = this
116+
117+ this.timer = timer
118+ this.timeline = timeline
119+
120+ this._timer = null
121+ this._nextArrival = Infinity
122+
123+ this._runReadyTasksBound = function () { return this$1._runReadyTasks(this$1.now()) }
124+}
125+
126+Scheduler.prototype.now = function now () {
127+ return this.timer.now()
128+}
129+
130+Scheduler.prototype.asap = function asap (task) {
131+ return this.schedule(0)(-1)(task)
132+}
133+
134+Scheduler.prototype.delay = function delay (delay$1) {
135+ var this$1 = this
136+
137+ return function (task) { return this$1.schedule(delay$1)(-1)(task) }
138+}
139+
140+Scheduler.prototype.periodic = function periodic (period) {
141+ var this$1 = this
142+
143+ return function (task) { return this$1.schedule(0)(period)(task) }
144+}
145+
146+Scheduler.prototype.schedule = function schedule (delay) {
147+ var this$1 = this
148+
149+ return function (period) {
150+ return function (task) {
151+ var now = this$1.now()
152+ var st = new ScheduledTask(now + Math.max(0, delay), period, task, this$1)
153+
154+ st.run = st.run.bind(st)
155+ st.dispose = st.dispose.bind(st)
156+
157+ this$1.timeline.add(st)
158+ this$1._scheduleNextRun(now)
159+ return st
160+ }
161+ }
162+}
163+
164+Scheduler.prototype.cancel = function cancel (task) {
165+ task.active = false
166+ if (this.timeline.remove(task)) {
167+ this._reschedule()
168+ }
169+}
170+
171+Scheduler.prototype.cancelAll = function cancelAll (f) {
172+ this.timeline.removeAll(f)
173+ this._reschedule()
174+}
175+
176+Scheduler.prototype._reschedule = function _reschedule () {
177+ if (this.timeline.isEmpty()) {
178+ this._unschedule()
179+ } else {
180+ this._scheduleNextRun(this.now())
181+ }
182+}
183+
184+Scheduler.prototype._unschedule = function _unschedule () {
185+ this.timer.clearTimer(this._timer)
186+ this._timer = null
187+}
188+
189+Scheduler.prototype._scheduleNextRun = function _scheduleNextRun (now) { // eslint-disable-line complexity
190+ if (this.timeline.isEmpty()) {
191+ return
192+ }
193+
194+ var nextArrival = this.timeline.nextArrival()
195+
196+ if (this._timer === null) {
197+ this._scheduleNextArrival(nextArrival, now)
198+ } else if (nextArrival < this._nextArrival) {
199+ this._unschedule()
200+ this._scheduleNextArrival(nextArrival, now)
201+ }
202+}
203+
204+Scheduler.prototype._scheduleNextArrival = function _scheduleNextArrival (nextArrival, now) {
205+ this._nextArrival = nextArrival
206+ var delay = Math.max(0, nextArrival - now)
207+ this._timer = this.timer.setTimer(this._runReadyTasksBound, delay)
208+}
209+
210+Scheduler.prototype._runReadyTasks = function _runReadyTasks (now) {
211+ this._timer = null
212+ this.timeline.runTasks(now, runTask$1)
213+ this._scheduleNextRun(this.now())
214+}
215+
216+/** @license MIT License (c) copyright 2010-2016 original author or authors */
217+/** @author Brian Cavalier */
218+/** @author John Hann */
219+
220+var Timeline = function Timeline () {
221+ this.tasks = []
222+}
223+
224+Timeline.prototype.nextArrival = function nextArrival () {
225+ return this.isEmpty() ? Infinity : this.tasks[0].time
226+}
227+
228+Timeline.prototype.isEmpty = function isEmpty () {
229+ return this.tasks.length === 0
230+}
231+
232+Timeline.prototype.add = function add (st) {
233+ insertByTime(st, this.tasks)
234+}
235+
236+Timeline.prototype.remove = function remove$$1 (st) {
237+ var i = binarySearch(st.time, this.tasks)
238+
239+ if (i >= 0 && i < this.tasks.length) {
240+ var at = findIndex(st, this.tasks[i].events)
241+ if (at >= 0) {
242+ this.tasks[i].events.splice(at, 1)
243+ return true
244+ }
245+ }
246+
247+ return false
248+}
249+
250+Timeline.prototype.removeAll = function removeAll$$1 (f) {
251+ var this$1 = this
252+
253+ for (var i = 0; i < this.tasks.length; ++i) {
254+ removeAllFrom(f, this$1.tasks[i])
255+ }
256+}
257+
258+Timeline.prototype.runTasks = function runTasks (t, runTask) {
259+ var this$1 = this
260+
261+ var tasks = this.tasks
262+ var l = tasks.length
263+ var i = 0
264+
265+ while (i < l && tasks[i].time <= t) {
266+ ++i
267+ }
268+
269+ this.tasks = tasks.slice(i)
270+
271+ // Run all ready tasks
272+ for (var j = 0; j < i; ++j) {
273+ this$1.tasks = runReadyTasks(runTask, tasks[j].events, this$1.tasks)
274+ }
275+}
276+
277+function runReadyTasks (runTask, events, tasks) { // eslint-disable-line complexity
278+ for (var i = 0; i < events.length; ++i) {
279+ var task = events[i]
280+
281+ if (task.active) {
282+ runTask(task)
283+
284+ // Reschedule periodic repeating tasks
285+ // Check active again, since a task may have canceled itself
286+ if (task.period >= 0 && task.active) {
287+ task.time = task.time + task.period
288+ insertByTime(task, tasks)
289+ }
290+ }
291+ }
292+
293+ return tasks
294+}
295+
296+function insertByTime (task, timeslots) { // eslint-disable-line complexity
297+ var l = timeslots.length
298+
299+ if (l === 0) {
300+ timeslots.push(newTimeslot(task.time, [task]))
301+ return
302+ }
303+
304+ var i = binarySearch(task.time, timeslots)
305+
306+ if (i >= l) {
307+ timeslots.push(newTimeslot(task.time, [task]))
308+ } else if (task.time === timeslots[i].time) {
309+ timeslots[i].events.push(task)
310+ } else {
311+ timeslots.splice(i, 0, newTimeslot(task.time, [task]))
312+ }
313+}
314+
315+function removeAllFrom (f, timeslot) {
316+ timeslot.events = removeAll(f, timeslot.events)
317+}
318+
319+function binarySearch (t, sortedArray) { // eslint-disable-line complexity
320+ var lo = 0
321+ var hi = sortedArray.length
322+ var mid, y
323+
324+ while (lo < hi) {
325+ mid = Math.floor((lo + hi) / 2)
326+ y = sortedArray[mid]
327+
328+ if (t === y.time) {
329+ return mid
330+ } else if (t < y.time) {
331+ hi = mid
332+ } else {
333+ lo = mid + 1
334+ }
335+ }
336+ return hi
337+}
338+
339+var newTimeslot = function (t, events) { return ({ time: t, events: events }) }
340+
341+function removeAll (f, a) {
342+ var l = a.length
343+ var b = new Array(l)
344+ var j = 0
345+ for (var x = (void 0), i = 0; i < l; ++i) {
346+ x = a[i]
347+ if (!f(x)) {
348+ b[j] = x
349+ ++j
350+ }
351+ }
352+
353+ b.length = j
354+ return b
355+}
356+
357+// findIndex :: a -> [a] -> Int
358+// find index of x in a, from the left
359+function findIndex (x, a) {
360+ for (var i = 0, l = a.length; i < l; ++i) {
361+ if (x === a[i]) {
362+ return i
363+ }
364+ }
365+ return -1
366+}
367+
368+var scheduler = new Scheduler(new ClockTimer(), new Timeline())
369+
370+exports.scheduler = scheduler
src/Control/Scheduler/Scheduler.pursnew file+32
@@ -0,0 +1,32 @@
1+module Control.Stream.Scheduler where
2+
3+import Control.Monad.Eff (Pure)
4+import Control.Stream.Disposable (Disposable)
5+import Control.Stream.Sink (Sink)
6+import Control.Stream.Time (Time)
7+import Data.Unit (Unit)
8+
9+newtype Scheduler = Scheduler
10+ { now :: Pure Time
11+ , asap :: Task -> ScheduledTask
12+ , delay :: Number -> Task -> ScheduledTask
13+ , periodic :: Number -> Task -> ScheduledTask
14+ , schedule :: Number -> Number -> Task -> ScheduledTask
15+ , cancel :: ScheduledTask -> Unit
16+ , cancelAll :: (ScheduledTask -> Boolean) -> Unit
17+ }
18+
19+type Task =
20+ { run :: Time -> Unit
21+ , dispose :: Pure Unit
22+ }
23+
24+type ScheduledTask =
25+ { run :: Pure Unit
26+ , dispose :: Pure Unit
27+ }
28+
29+foreign import scheduler :: Scheduler
30+foreign import scheduleTasks :: (Task -> ScheduledTask) -> Array Task -> Disposable
31+foreign import eventTask :: forall a. Sink a -> a -> Task
32+foreign import endTask :: forall a. Sink a -> Task
src/Control/Sink/Sink.pursnew file+9
@@ -0,0 +1,9 @@
1+module Control.Stream.Sink (Sink(..)) where
2+
3+import Control.Stream.Time (Time)
4+import Data.Unit (Unit)
5+
6+newtype Sink a = Sink
7+ { event :: Time -> a -> Unit
8+ , end :: Time -> Unit
9+ }
src/Control/Stream.jsdeleted−1384
@@ -1,1384 +0,0 @@
1−'use strict';
2−
3−Object.defineProperty(exports, '__esModule', { value: true });
4−
5−/** @license MIT License (c) copyright 2010-2016 original author or authors */
6−
7−// Non-mutating array operations
8−
9−// cons :: a -> [a] -> [a]
10−// a with x prepended
11−
12−
13−// append :: a -> [a] -> [a]
14−// a with x appended
15−function append (x, a) {
16− var l = a.length;
17− var b = new Array(l + 1);
18− for (var i = 0; i < l; ++i) {
19− b[i] = a[i];
20− }
21−
22− b[l] = x;
23− return b
24−}
25−
26−// drop :: Int -> [a] -> [a]
27−// drop first n elements
28−function drop (n, a) { // eslint-disable-line complexity
29− if (n < 0) {
30− throw new TypeError('n must be >= 0')
31− }
32−
33− var l = a.length;
34− if (n === 0 || l === 0) {
35− return a
36− }
37−
38− if (n >= l) {
39− return []
40− }
41−
42− return unsafeDrop(n, a, l - n)
43−}
44−
45−// unsafeDrop :: Int -> [a] -> Int -> [a]
46−// Internal helper for drop
47−function unsafeDrop (n, a, l) {
48− var b = new Array(l);
49− for (var i = 0; i < l; ++i) {
50− b[i] = a[n + i];
51− }
52− return b
53−}
54−
55−// tail :: [a] -> [a]
56−// drop head element
57−function tail (a) {
58− return drop(1, a)
59−}
60−
61−// copy :: [a] -> [a]
62−// duplicate a (shallow duplication)
63−
64−
65−// map :: (a -> b) -> [a] -> [b]
66−// transform each element with f
67−function map (f, a) {
68− var l = a.length;
69− var b = new Array(l);
70− for (var i = 0; i < l; ++i) {
71− b[i] = f(a[i]);
72− }
73− return b
74−}
75−
76−// reduce :: (a -> b -> a) -> a -> [b] -> a
77−// accumulate via left-fold
78−function reduce (f, z, a) {
79− var r = z;
80− for (var i = 0, l = a.length; i < l; ++i) {
81− r = f(r, a[i], i);
82− }
83− return r
84−}
85−
86−// replace :: a -> Int -> [a]
87−// replace element at index
88−
89−
90−// remove :: Int -> [a] -> [a]
91−// remove element at index
92−function remove (i, a) { // eslint-disable-line complexity
93− if (i < 0) {
94− throw new TypeError('i must be >= 0')
95− }
96−
97− var l = a.length;
98− if (l === 0 || i >= l) { // exit early if index beyond end of array
99− return a
100− }
101−
102− if (l === 1) { // exit early if index in bounds and length === 1
103− return []
104− }
105−
106− return unsafeRemove(i, a, l - 1)
107−}
108−
109−// unsafeRemove :: Int -> [a] -> Int -> [a]
110−// Internal helper to remove element at index
111−function unsafeRemove (i, a, l) {
112− var b = new Array(l);
113− var j;
114− for (j = 0; j < i; ++j) {
115− b[j] = a[j];
116− }
117− for (j = i; j < l; ++j) {
118− b[j] = a[j + 1];
119− }
120−
121− return b
122−}
123−
124−// removeAll :: (a -> boolean) -> [a] -> [a]
125−// remove all elements matching a predicate
126−function removeAll (f, a) {
127− var l = a.length;
128− var b = new Array(l);
129− var j = 0;
130− for (var x = (void 0), i = 0; i < l; ++i) {
131− x = a[i];
132− if (!f(x)) {
133− b[j] = x;
134− ++j;
135− }
136− }
137−
138− b.length = j;
139− return b
140−}
141−
142−// findIndex :: a -> [a] -> Int
143−// find index of x in a, from the left
144−function findIndex (x, a) {
145− for (var i = 0, l = a.length; i < l; ++i) {
146− if (x === a[i]) {
147− return i
148− }
149− }
150− return -1
151−}
152−
153−// isArrayLike :: * -> boolean
154−// Return true iff x is array-like
155−
156−/** @license MIT License (c) copyright 2010-2016 original author or authors */
157−
158−// id :: a -> a
159−var id = function (x) { return x; };
160−
161−// compose :: (b -> c) -> (a -> b) -> (a -> c)
162−
163−
164−// apply :: (a -> b) -> a -> b
165−
166−
167−// curry2 :: ((a, b) -> c) -> (a -> b -> c)
168−function curry2 (f) {
169− function curried (a, b) {
170− switch (arguments.length) {
171− case 0: return curried
172− case 1: return function (b) { return f(a, b); }
173− default: return f(a, b)
174− }
175− }
176− return curried
177−}
178−
179−// curry3 :: ((a, b, c) -> d) -> (a -> b -> c -> d)
180−function curry3 (f) {
181− function curried (a, b, c) { // eslint-disable-line complexity
182− switch (arguments.length) {
183− case 0: return curried
184− case 1: return curry2(function (b, c) { return f(a, b, c); })
185− case 2: return function (c) { return f(a, b, c); }
186− default:return f(a, b, c)
187− }
188− }
189− return curried
190−}
191−
192−// curry4 :: ((a, b, c, d) -> e) -> (a -> b -> c -> d -> e)
193−
194−/** @license MIT License (c) copyright 2016 original author or authors */
195−
196−var Disposable = function Disposable (dispose, data) {
197− this._dispose = dispose;
198− this._data = data;
199−
200− this.dispose = function () { return dispose(data); };
201−};
202−
203−var MemoizedDisposable = function MemoizedDisposable (disposable) {
204− this.disposed = false;
205− this.value = undefined;
206− this.disposable = disposable;
207−};
208−
209−MemoizedDisposable.prototype.dispose = function dispose () {
210− if (!this.disposed) {
211− this.disposed = true;
212− this.value = this.disposable.dispose();
213− this.disposable = undefined;
214− }
215−
216− return this.value
217−};
218−
219−function disposeSafely (disposable) {
220− disposable.dispose();
221−}
222−
223−var createDisposable = curry2(function (dispose, data) { return new MemoizedDisposable(new Disposable(dispose, data)); });
224−
225−var emptyDisposable = new Disposable(id, undefined);
226−
227−var disposeAll = function (disposables) { return createDisposable(curry2(map)(disposeSafely), disposables); };
228−
229−/** @license MIT License (c) copyright 2010-2016 original author or authors */
230−/** @author Brian Cavalier */
231−/** @author John Hann */
232−
233−/*global setTimeout, clearTimeout*/
234−
235−function defer (task) {
236− return Promise.resolve(task).then(runTask)
237−}
238−
239−function runTask (task) {
240− return task.run()
241−}
242−
243−var ClockTimer = function ClockTimer () {
244− this.now = Date.now;
245−};
246−
247−ClockTimer.prototype.setTimer = function setTimer (f, dt) {
248− return dt <= 0 ? runAsap(f) : setTimeout(f, dt)
249−};
250−
251−ClockTimer.prototype.clearTimer = function clearTimer (t) {
252− return t instanceof Asap ? t.cancel() : clearTimeout(t)
253−};
254−
255−var Asap = function Asap (f) {
256− this.f = f;
257− this.active = true;
258−};
259−
260−Asap.prototype.run = function run () {
261− return this.active && this.f()
262−};
263−
264−Asap.prototype.cancel = function cancel () {
265− this.active = false;
266−};
267−
268−function runAsap (f) {
269− var task = new Asap(f);
270− defer(task);
271− return task
272−}
273−
274−/* */
275−var ScheduledTask = function ScheduledTask (delay, period, task, scheduler) {
276− this.time = delay;
277− this.period = period;
278− this.task = task;
279− this.scheduler = scheduler;
280− this.active = true;
281−};
282−
283−ScheduledTask.prototype.run = function run () {
284− this.task.run(this.time);
285−};
286−
287−ScheduledTask.prototype.dispose = function dispose () {
288− this.scheduler.cancel(this);
289− this.task.dispose();
290−};
291−
292−/* */
293−function runTask$1 (task) {
294− try {
295− return task.run()
296− } catch (e) {
297− console.error(e);
298− }
299−}
300−
301−var Scheduler = function Scheduler (timer, timeline) {
302− var this$1 = this;
303−
304− this.timer = timer;
305− this.timeline = timeline;
306−
307− this._timer = null;
308− this._nextArrival = Infinity;
309−
310− this._runReadyTasksBound = function () { return this$1._runReadyTasks(this$1.now()); };
311−};
312−
313−Scheduler.prototype.now = function now () {
314− return this.timer.now()
315−};
316−
317−Scheduler.prototype.asap = function asap (task) {
318− return this.schedule(0)(-1)(task)
319−};
320−
321−Scheduler.prototype.delay = function delay (delay$1) {
322− var this$1 = this;
323−
324− return function (task) { return this$1.schedule(delay$1)(-1)(task); }
325−};
326−
327−Scheduler.prototype.periodic = function periodic (period) {
328− var this$1 = this;
329−
330− return function (task) { return this$1.schedule(0)(period)(task); }
331−};
332−
333−Scheduler.prototype.schedule = function schedule (delay) {
334− var this$1 = this;
335−
336− return function (period) { return function (task) {
337− var now = this$1.now();
338− var st = new ScheduledTask(now + Math.max(0, delay), period, task, this$1);
339−
340− this$1.timeline.add(st);
341− this$1._scheduleNextRun(now);
342− return st
343− }; }
344−};
345−
346−Scheduler.prototype.cancel = function cancel (task) {
347− task.active = false;
348− if (this.timeline.remove(task)) {
349− this._reschedule();
350− }
351−};
352−
353−Scheduler.prototype.cancelAll = function cancelAll (f) {
354− this.timeline.removeAll(f);
355− this._reschedule();
356−};
357−
358−Scheduler.prototype._reschedule = function _reschedule () {
359− if (this.timeline.isEmpty()) {
360− this._unschedule();
361− } else {
362− this._scheduleNextRun(this.now());
363− }
364−};
365−
366−Scheduler.prototype._unschedule = function _unschedule () {
367− this.timer.clearTimer(this._timer);
368− this._timer = null;
369−};
370−
371−Scheduler.prototype._scheduleNextRun = function _scheduleNextRun (now) { // eslint-disable-line complexity
372− if (this.timeline.isEmpty()) {
373− return
374− }
375−
376− var nextArrival = this.timeline.nextArrival();
377−
378− if (this._timer === null) {
379− this._scheduleNextArrival(nextArrival, now);
380− } else if (nextArrival < this._nextArrival) {
381− this._unschedule();
382− this._scheduleNextArrival(nextArrival, now);
383− }
384−};
385−
386−Scheduler.prototype._scheduleNextArrival = function _scheduleNextArrival (nextArrival, now) {
387− this._nextArrival = nextArrival;
388− var delay = Math.max(0, nextArrival - now);
389− this._timer = this.timer.setTimer(this._runReadyTasksBound, delay);
390−};
391−
392−Scheduler.prototype._runReadyTasks = function _runReadyTasks (now) {
393− this._timer = null;
394− this.timeline.runTasks(now, runTask$1);
395− this._scheduleNextRun(this.now());
396−};
397−
398−/** @license MIT License (c) copyright 2010-2016 original author or authors */
399−/** @author Brian Cavalier */
400−/** @author John Hann */
401−
402−var Timeline = function Timeline () {
403− this.tasks = [];
404−};
405−
406−Timeline.prototype.nextArrival = function nextArrival () {
407− return this.isEmpty() ? Infinity : this.tasks[0].time
408−};
409−
410−Timeline.prototype.isEmpty = function isEmpty () {
411− return this.tasks.length === 0
412−};
413−
414−Timeline.prototype.add = function add (st) {
415− insertByTime(st, this.tasks);
416−};
417−
418−Timeline.prototype.remove = function remove$$1 (st) {
419− var i = binarySearch(st.time, this.tasks);
420−
421− if (i >= 0 && i < this.tasks.length) {
422− var at = findIndex(st, this.tasks[i].events);
423− if (at >= 0) {
424− this.tasks[i].events.splice(at, 1);
425− return true
426− }
427− }
428−
429− return false
430−};
431−
432−Timeline.prototype.removeAll = function removeAll$$1 (f) {
433− var this$1 = this;
434−
435− for (var i = 0; i < this.tasks.length; ++i) {
436− removeAllFrom(f, this$1.tasks[i]);
437− }
438−};
439−
440−Timeline.prototype.runTasks = function runTasks (t, runTask) {
441− var this$1 = this;
442−
443− var tasks = this.tasks;
444− var l = tasks.length;
445− var i = 0;
446−
447− while (i < l && tasks[i].time <= t) {
448− ++i;
449− }
450−
451− this.tasks = tasks.slice(i);
452−
453− // Run all ready tasks
454− for (var j = 0; j < i; ++j) {
455− this$1.tasks = runReadyTasks(runTask, tasks[j].events, this$1.tasks);
456− }
457−};
458−
459−function runReadyTasks (runTask, events, tasks) { // eslint-disable-line complexity
460− for (var i = 0; i < events.length; ++i) {
461− var task = events[i];
462−
463− if (task.active) {
464− runTask(task);
465−
466− // Reschedule periodic repeating tasks
467− // Check active again, since a task may have canceled itself
468− if (task.period >= 0 && task.active) {
469− task.time = task.time + task.period;
470− insertByTime(task, tasks);
471− }
472− }
473− }
474−
475− return tasks
476−}
477−
478−function insertByTime (task, timeslots) { // eslint-disable-line complexity
479− var l = timeslots.length;
480−
481− if (l === 0) {
482− timeslots.push(newTimeslot(task.time, [task]));
483− return
484− }
485−
486− var i = binarySearch(task.time, timeslots);
487−
488− if (i >= l) {
489− timeslots.push(newTimeslot(task.time, [task]));
490− } else if (task.time === timeslots[i].time) {
491− timeslots[i].events.push(task);
492− } else {
493− timeslots.splice(i, 0, newTimeslot(task.time, [task]));
494− }
495−}
496−
497−function removeAllFrom (f, timeslot) {
498− timeslot.events = removeAll(f, timeslot.events);
499−}
500−
501−function binarySearch (t, sortedArray) { // eslint-disable-line complexity
502− var lo = 0;
503− var hi = sortedArray.length;
504− var mid, y;
505−
506− while (lo < hi) {
507− mid = Math.floor((lo + hi) / 2);
508− y = sortedArray[mid];
509−
510− if (t === y.time) {
511− return mid
512− } else if (t < y.time) {
513− hi = mid;
514− } else {
515− lo = mid + 1;
516− }
517− }
518− return hi
519−}
520−
521−var newTimeslot = function (t, events) { return ({ time: t, events: events }); };
522−
523−var propagateTask =
524− curry3(function (run, value, sink) { return new PropagateTask(run, value, sink); });
525−
526−var eventTask =
527− curry2(function (value, sink) { return propagateTask(runEvent, value, sink); });
528−
529−var endTask = function (sink) { return propagateTask(runEnd, void 0, sink); };
530−
531−var PropagateTask = function PropagateTask (run, value, sink) {
532− var active = true;
533− this.sink = sink;
534−
535− this.dispose = function () { active = false; };
536− this.run = function (t) {
537− if (!active) { return }
538−
539− run(t, value, sink);
540− };
541−};
542−
543−function runEvent (time, value, sink) {
544− sink.event(time)(value);
545−}
546−
547−function runEnd (time, value, sink) {
548− sink.end(time, value);
549−}
550−
551−var defaultScheduler = new Scheduler(new ClockTimer(), new Timeline());
552−
553−var scheduleTasks = curry2(function (tasks, scheduler) {
554− var scheduledTasks = tasks.map(function (task) { return scheduler.asap(task); });
555−
556− return {
557− dispose: function () { return scheduledTasks.forEach(function (task) { return task.dispose(); }); }
558− }
559−});
560−
561−var SettableDisposable = function SettableDisposable () {
562− this.disposable = void 0;
563− this.disposed = false;
564− this.result = void 0;
565−};
566−
567−SettableDisposable.prototype.setDisposable = function setDisposable (disposable) {
568− this.disposable = disposable;
569−
570− if (this.disposed) {
571− this.result = disposable.dispose();
572− }
573−};
574−
575−SettableDisposable.prototype.dispose = function dispose () {
576− if (this.disposed) {
577− return this.result
578− }
579−
580− this.disposed = true;
581−
582− if (this.disposable !== void 0) {
583− this.result = this.disposable.dispose();
584− }
585−
586− return this.result
587−};
588−
589−function runEffects (stream, scheduler) {
590− return runSourceEffects(stream.source, scheduler)
591−}
592−
593−var runSourceEffects = function (source, scheduler) { return runSource(source, scheduler); };
594−
595−function runSource (source, scheduler) {
596− var disposable = new SettableDisposable();
597− var observer = new RunEffectsSink(disposable);
598−
599− disposable.setDisposable(source.run(observer)(scheduler));
600−
601− return {}
602−}
603−
604−var RunEffectsSink = function RunEffectsSink (disposable) {
605− this._disposable = disposable;
606− this.active = true;
607−};
608−
609−RunEffectsSink.prototype.event = function event (t) {
610− return function (x) {}
611−};
612−
613−RunEffectsSink.prototype.end = function end (t) {
614− this.active = false;
615− Promise.resolve(this._disposable).then(function (d) { return d.dispose(); });
616−};
617−
618−var drain = function (stream) { return function () { return runEffects(stream, defaultScheduler); }; };
619−
620−var LinkedList = function LinkedList () {
621− this.head = null;
622− this.length = 0;
623−};
624−
625−LinkedList.prototype.add = function add (x) {
626− if (this.head !== null) {
627− this.head.prev = x;
628− x.next = this.head;
629− }
630− this.head = x;
631− ++this.length;
632−};
633−
634−LinkedList.prototype.remove = function remove (x) { // eslint-disable-linecomplexity
635− --this.length;
636− if (x === this.head) {
637− this.head = this.head.next;
638− }
639− if (x.next !== null) {
640− x.next.prev = x.prev;
641− x.next = null;
642− }
643− if (x.prev !== null) {
644− x.prev.next = x.next;
645− x.prev = null;
646− }
647−};
648−
649−LinkedList.prototype.isEmpty = function isEmpty () {
650− return this.length === 0
651−};
652−
653−LinkedList.prototype.dispose = function dispose () {
654− if (this.isEmpty()) { return }
655−
656− var x = this.head;
657− this.head = null;
658− this.length = 0;
659−
660− while (x !== null) {
661− x.dispose();
662− x = x.next;
663− }
664−};
665−
666−var Stream = function Stream (source) {
667− this.source = source;
668−};
669−
670−/** @license MIT License (c) copyright 2010-2016 original author or authors */
671−/** @author Brian Cavalier */
672−/** @author John Hann */
673−
674−var flatMap =
675− curry2(function (stream, f) { return mergeMapConcurrently(f, Infinity, stream); });
676−
677−var mergeConcurrently = curry2(function (concurrency, stream) { return mergeMapConcurrently(id, concurrency, stream); });
678−
679−var join =
680− mergeConcurrently(Infinity);
681−
682−var mergeMapConcurrently = curry3(function (f, concurrency, stream) { return new Stream(new MergeConcurrently(f, concurrency, stream.source)); });
683−
684−var MergeConcurrently = function MergeConcurrently (f, concurrency, source) {
685− this.f = f;
686− this.concurrency = concurrency;
687− this.source = source;
688−};
689−
690−MergeConcurrently.prototype.run = function run (sink) {
691− var this$1 = this;
692−
693− return function (scheduler) { return new Outer(this$1.f, this$1.concurrency, this$1.source, sink, scheduler); }
694−};
695−
696−var Outer = function Outer (f, concurrency, source, sink, scheduler) {
697− this.f = f;
698− this.concurrency = concurrency;
699− this.sink = sink;
700− this.scheduler = scheduler;
701− this.pending = [];
702− this.current = new LinkedList();
703− this.disposable = new MemoizedDisposable(source.run(this)(scheduler));
704− this.active = true;
705−};
706−
707−Outer.prototype.event = function event (t) {
708− var this$1 = this;
709−
710− return function (x) { return this$1._addInner(t, x); }
711−};
712−
713−Outer.prototype._addInner = function _addInner (t, x) {
714− if (this.current.length < this.concurrency) {
715− this._startInner(t, x);
716− } else {
717− this.pending.push(x);
718− }
719−};
720−
721−Outer.prototype._startInner = function _startInner (t, x) {
722− try {
723− this._initInner(t, x);
724− } catch (e) {
725− this.error(t, e);
726− }
727−};
728−
729−Outer.prototype._initInner = function _initInner (t, x) {
730− var innerSink = new Inner(t, this, this.sink);
731− innerSink.disposable = mapAndRun(this.f, x, innerSink, this.scheduler);
732− this.current.add(innerSink);
733−};
734−
735−Outer.prototype.end = function end (t) {
736− this.active = false;
737− this.disposable.dispose();
738− this._checkEnd(t);
739−};
740−
741−Outer.prototype.dispose = function dispose () {
742− this.active = false;
743− this.pending.length = 0;
744− this.disposable.dispose();
745− this.current.dispose();
746−};
747−
748−Outer.prototype._endInner = function _endInner (t, inner) {
749− this.current.remove(inner);
750− inner.dispose();
751−
752− if (this.pending.length === 0) {
753− this._checkEnd(t);
754− } else {
755− this._startInner(t, this.pending.shift());
756− }
757−};
758−
759−Outer.prototype._checkEnd = function _checkEnd (t) {
760− if (!this.active && this.current.isEmpty()) {
761− this.sink.end(t);
762− }
763−};
764−
765−var mapAndRun = function (f, x, sink, scheduler) { return f(x).source.run(sink)(scheduler); };
766−
767−var Inner = function Inner (time, outer, sink) {
768− this.prev = this.next = null;
769− this.time = time;
770− this.outer = outer;
771− this.sink = sink;
772− this.disposable = void 0;
773−};
774−
775−Inner.prototype.event = function event (t) {
776− var this$1 = this;
777−
778− return function (x) { return this$1.sink.event(Math.max(t, this$1.time))(x); }
779−};
780−
781−Inner.prototype.end = function end (t) {
782− this.outer._endInner(Math.max(t, this.time), this);
783−};
784−
785−Inner.prototype.dispose = function dispose () {
786− return this.disposable.dispose()
787−};
788−
789−var IndexSink = function IndexSink (i, sink) {
790− this.sink = sink;
791− this.index = i;
792− this.active = true;
793− this.value = void 0;
794−};
795−
796−IndexSink.prototype.event = function event (t) {
797− var this$1 = this;
798−
799− return function (x) {
800− if (!this$1.active) { return }
801−
802− this$1.value = x;
803− this$1.sink.event(t)(this$1);
804− }
805−};
806−
807−IndexSink.prototype.end = function end (t) {
808− if (!this.active) { return }
809−
810− this.active = false;
811− this.sink.end(t, { index: this.index });
812−};
813−
814−/** @license MIT License (c) copyright 2010-2016 original author or authors */
815−/** @author Brian Cavalier */
816−/** @author John Hann */
817−
818−var combine2 = function (f) { return function (s1) { return function (s2) { return combineArray(f, [s1, s2]); }; }; };
819−var combine3 = function (f) { return function (s1) { return function (s2) { return function (s3) { return combineArray(f, [s1, s2, s3]); }; }; }; };
820−var combine4 = function (f) { return function (s1) { return function (s2) { return function (s3) { return function (s4) { return combineArray(f, [s1, s2, s3, s4]); }; }; }; }; };
821−var combine5 = function (f) { return function (s1) { return function (s2) { return function (s3) { return function (s4) { return function (s5) { return combineArray(f, [s1, s2, s3, s4, s5]); }; }; }; }; }; };
822−
823−function combineArray (f, streams) {
824− return new Stream(combineSources(f, streams))
825−}
826−
827−function combineSources (f, streams) {
828− return new Combine(f, map(getSource, streams))
829−}
830−
831−function getSource (stream) {
832− return stream.source
833−}
834−
835−function Combine (f, sources) {
836− this.f = f;
837− this.sources = sources;
838−}
839−
840−Combine.prototype.run = function (sink) {
841− var this$1 = this;
842−
843− return function (scheduler) {
844− var l = this$1.sources.length;
845− var disposables = new Array(l);
846− var sinks = new Array(l);
847−
848− var mergeSink = new CombineSink(disposables, sinks, sink, this$1.f);
849−
850− for (var indexSink, i = 0; i < l; ++i) {
851− indexSink = sinks[i] = new IndexSink(i, mergeSink);
852− disposables[i] = this$1.sources[i].run(indexSink)(scheduler);
853− }
854−
855− return disposeAll(disposables)
856− }
857−};
858−
859−function CombineSink (disposables, sinks, sink, f) {
860− var this$1 = this;
861−
862− this.sink = sink;
863− this.disposables = disposables;
864− this.sinks = sinks;
865− this.f = f;
866−
867− var l = sinks.length;
868− this.awaiting = l;
869− this.values = new Array(l);
870− this.hasValue = new Array(l);
871−
872− for (var i = 0; i < l; ++i) {
873− this$1.hasValue[i] = false;
874− }
875−
876− this.activeCount = sinks.length;
877−}
878−
879−CombineSink.prototype.event = function (t) {
880− var this$1 = this;
881−
882− return function (indexedValue) {
883− var i = indexedValue.index;
884− var awaiting = this$1._updateReady(i);
885−
886− this$1.values[i] = indexedValue.value;
887− if (awaiting === 0) {
888− var value = tail(this$1.values).reduce(function (f, x) { return f(x); }, this$1.f(this$1.values[0]));
889− this$1.sink.event(t)(value);
890− }
891− }
892−};
893−
894−CombineSink.prototype._updateReady = function (index) {
895− if (this.awaiting > 0) {
896− if (!this.hasValue[index]) {
897− this.hasValue[index] = true;
898− this.awaiting -= 1;
899− }
900− }
901− return this.awaiting
902−};
903−
904−CombineSink.prototype.end = function (t, indexedValue) {
905− this.disposables[indexedValue.index].dispose();
906−
907− if (--this.activeCount === 0) {
908− this.sink.end(t);
909− }
910−};
911−
912−/** @license MIT License (c) copyright 2010-2016 original author or authors */
913−/** @author Brian Cavalier */
914−/** @author John Hann */
915−
916−/**
917− * @param {Array} streams array of stream to merge
918− * @returns {Stream} stream containing events from all input observables
919− * in time order. If two events are simultaneous they will be merged in
920− * arbitrary order.
921− */
922−function merge (streams) {
923− return new Stream(mergeSources(streams))
924−}
925−
926−/**
927− * This implements fusion/flattening for merge. It will
928− * fuse adjacent merge operations. For example:
929− * - a.merge(b).merge(c) effectively becomes merge(a, b, c)
930− * - merge(a, merge(b, c)) effectively becomes merge(a, b, c)
931− * It does this by concatenating the sources arrays of
932− * any nested Merge sources, in effect "flattening" nested
933− * merge operations into a single merge.
934− */
935−function mergeSources (streams) {
936− return new Merge(reduce(appendSources, [], streams))
937−}
938−
939−function appendSources (sources, stream) {
940− var source = stream.source;
941− return source instanceof Merge
942− ? sources.concat(source.sources)
943− : sources.concat(source)
944−}
945−
946−function Merge (sources) {
947− this.sources = sources;
948−}
949−
950−Merge.prototype.run = function (sink, scheduler) {
951− var this$1 = this;
952−
953− var l = this.sources.length;
954− var disposables = new Array(l);
955− var sinks = new Array(l);
956−
957− var mergeSink = new MergeSink(disposables, sinks, sink);
958−
959− for (var indexSink, i = 0; i < l; ++i) {
960− indexSink = sinks[i] = new IndexSink(i, mergeSink);
961− disposables[i] = this$1.sources[i].run(indexSink, scheduler);
962− }
963−
964− return disposeAll(disposables)
965−};
966−
967−function MergeSink (disposables, sinks, sink) {
968− this.sink = sink;
969− this.disposables = disposables;
970− this.activeCount = sinks.length;
971−}
972−
973−MergeSink.prototype.event = function (t, indexValue) {
974− this.sink.event(t, indexValue.value);
975−};
976−
977−MergeSink.prototype.end = function (t, indexedValue) {
978− dispose.tryDispose(t, this.disposables[indexedValue.index], this.sink);
979− if (--this.activeCount === 0) {
980− this.sink.end(t, indexedValue.value);
981− }
982−};
983−
984−var concat = function (s1) { return function (s2) { return continueWith(function () { return s2; })(s1); }; };
985−
986−var continueWith = function (f) { return function (stream) { return new Stream(new ContinueWith(f, stream.source)); }; };
987−
988−function ContinueWith (f, source) {
989− this.f = f;
990− this.source = source;
991−}
992−
993−ContinueWith.prototype.run = function (sink) {
994− var this$1 = this;
995−
996− return function (scheduler) {
997− return new ContinueWithSink(this$1.f, this$1.source, sink, scheduler)
998− }
999−};
1000−
1001−function ContinueWithSink (f, source, sink, scheduler) {
1002− this.f = f;
1003− this.sink = sink;
1004− this.scheduler = scheduler;
1005− this.active = true;
1006− this.disposable = new MemoizedDisposable(source.run(this)(scheduler));
1007−}
1008−
1009−ContinueWithSink.prototype.event = function (t) {
1010− var this$1 = this;
1011−
1012− return function (x) {
1013− if (!this$1.active) {
1014− return
1015− }
1016− this$1.sink.event(t)(x);
1017− }
1018−};
1019−
1020−ContinueWithSink.prototype.end = function (t, x) {
1021− if (!this.active) {
1022− return
1023− }
1024−
1025− this.disposable.dispose();
1026− this._startNext(t, x, this.sink);
1027−};
1028−
1029−ContinueWithSink.prototype._startNext = function (t, x, sink) {
1030− this._continue(this.f, x, sink);
1031−};
1032−
1033−ContinueWithSink.prototype._continue = function (f, x, sink) {
1034− return f(x).source.run(sink)(this.scheduler)
1035−};
1036−
1037−ContinueWithSink.prototype.dispose = function () {
1038− this.active = false;
1039− return this.disposable.dispose()
1040−};
1041−
1042−/**
1043− * Given a stream of streams, return a new stream that adopts the behavior
1044− * of the most recent inner stream.
1045− * @param {Stream} stream of streams on which to switch
1046− * @returns {Stream} switching stream
1047− */
1048−function switchLatest (stream) {
1049− return new Stream(new Switch(stream.source))
1050−}
1051−
1052−function Switch (source) {
1053− this.source = source;
1054−}
1055−
1056−Switch.prototype.run = function (sink) {
1057− var this$1 = this;
1058−
1059− return function (scheduler) {
1060− var switchSink = new SwitchSink(sink, scheduler);
1061− return disposeAll([switchSink, this$1.source.run(switchSink)(scheduler)])
1062− }
1063−};
1064−
1065−function SwitchSink (sink, scheduler) {
1066− this.sink = sink;
1067− this.scheduler = scheduler;
1068− this.current = null;
1069− this.ended = false;
1070−}
1071−
1072−SwitchSink.prototype.event = function (t) {
1073− var this$1 = this;
1074−
1075− return function (stream) {
1076− this$1._disposeCurrent(t); // TODO: capture the result of this dispose
1077− this$1.current = new Segment(t, Infinity, this$1, this$1.sink);
1078− this$1.current.disposable = stream.source.run(this$1.current)(this$1.scheduler);
1079− }
1080−};
1081−
1082−SwitchSink.prototype.end = function (t, x) {
1083− this.ended = true;
1084− this._checkEnd(t, x);
1085−};
1086−
1087−SwitchSink.prototype.dispose = function () {
1088− return this._disposeCurrent(this.scheduler.now())
1089−};
1090−
1091−SwitchSink.prototype._disposeCurrent = function (t) {
1092− if (this.current !== null) {
1093− return this.current._dispose(t)
1094− }
1095−};
1096−
1097−SwitchSink.prototype._disposeInner = function (t, inner) {
1098− inner._dispose(t); // TODO: capture the result of this dispose
1099− if (inner === this.current) {
1100− this.current = null;
1101− }
1102−};
1103−
1104−SwitchSink.prototype._checkEnd = function (t, x) {
1105− if (this.ended && this.current === null) {
1106− this.sink.end(t, x);
1107− }
1108−};
1109−
1110−SwitchSink.prototype._endInner = function (t, x, inner) {
1111− this._disposeInner(t, inner);
1112− this._checkEnd(t, x);
1113−};
1114−
1115−function Segment (min, max, outer, sink) {
1116− this.min = min;
1117− this.max = max;
1118− this.outer = outer;
1119− this.sink = sink;
1120− this.disposable = emptyDisposable;
1121−}
1122−
1123−Segment.prototype.event = function (t) {
1124− var this$1 = this;
1125−
1126− return function (x) {
1127− if (t < this$1.max) {
1128− this$1.sink.event(Math.max(t, this$1.min))(x);
1129− }
1130− }
1131−};
1132−
1133−Segment.prototype.end = function (t, x) {
1134− this.outer._endInner(Math.max(t, this.min), x, this);
1135−};
1136−
1137−Segment.prototype._dispose = function (t) {
1138− this.max = t;
1139− this.disposable.dispose();
1140−};
1141−
1142−var skipRepeatsWith = function (equals) { return function (stream) { return new Stream(new SkipRepeats(equals, stream)); }; };
1143−
1144−var SkipRepeats = function SkipRepeats (equals, source) {
1145− this.equals = equals;
1146− this.source = source;
1147−};
1148−
1149−SkipRepeats.prototype.run = function run (sink) {
1150− var this$1 = this;
1151−
1152− return function (scheduler) { return this$1.source.run(new SkipRepeatsSink(this$1.equals, sink), scheduler); }
1153−};
1154−
1155−var SkipRepeatsSink = function SkipRepeatsSink (equals, sink) {
1156− this.equals = equals;
1157− this.value = void 0;
1158− this.init = true;
1159−};
1160−
1161−SkipRepeatsSink.prototype.event = function event (t) {
1162− var this$1 = this;
1163−
1164− return function (x) {
1165− if (this$1.init) {
1166− this$1.init = false;
1167− this$1.value = x;
1168− this$1.sink.event(t, x);
1169− } else if (!this$1.equals(this$1.value)(x)) {
1170− this$1.value = x;
1171− this$1.sink.event(t)(x);
1172− }
1173− }
1174−};
1175−
1176−SkipRepeatsSink.prototype.end = function end (t) {
1177− this.sink.end(t);
1178−};
1179−
1180−var delay = function (delayTime) { return function (stream) { return delayTime <= 0 ? stream : new Stream(new Delay(delayTime, stream.source)); }; };
1181−
1182−var Delay = function Delay (dt, source) {
1183− this.dt = dt;
1184− this.source = source;
1185−};
1186−
1187−Delay.prototype.run = function run (sink) {
1188− var this$1 = this;
1189−
1190− return function (scheduler) {
1191− var delaySink = new DelaySink(this$1.dt, sink, scheduler);
1192− return disposeAll([delaySink, this$1.source.run(delaySink)(scheduler)])
1193− }
1194−};
1195−
1196−var DelaySink = function DelaySink (dt, sink, scheduler) {
1197− this.dt = dt;
1198− this.sink = sink;
1199− this.scheduler = scheduler;
1200−};
1201−
1202−DelaySink.prototype.dispose = function dispose () {
1203− var this$1 = this;
1204−
1205− this.scheduler.cancelAll(function (task) { return task.sink === this$1.sink; });
1206−};
1207−
1208−DelaySink.prototype.event = function event (t) {
1209− var this$1 = this;
1210−
1211− return function (x) { return this$1.scheduler.delay(this$1.dt)(eventTask(x)(this$1.sink)); }
1212−};
1213−
1214−DelaySink.prototype.end = function end (t) {
1215− this.scheduler.delay(this.dt)(endTask(this.sink));
1216−};
1217−
1218−var dispose$1 = function (disposable) { return disposable.dispose(); };
1219−
1220−var emptyDisposable$1 = {
1221− dispose: function dispose () {}
1222−};
1223−
1224−var MulticastDisposable = function MulticastDisposable (source, sink) {
1225− this.source = source;
1226− this.sink = sink;
1227− this.disposed = false;
1228−};
1229−
1230−MulticastDisposable.prototype.dispose = function dispose () {
1231− if (this.disposed) {
1232− return
1233− }
1234− this.disposed = true;
1235− var remaining = this.source.remove(this.sink);
1236− return remaining === 0 && this.source._dispose()
1237−};
1238−
1239−var MulticastSource = function MulticastSource (source) {
1240− this.source = source;
1241− this.sinks = [];
1242− this._disposable = emptyDisposable$1;
1243−};
1244−
1245−MulticastSource.prototype.run = function run (sink) {
1246− var this$1 = this;
1247−
1248− return function (scheduler) {
1249− var n = this$1.add(sink, scheduler);
1250− if (n === 1) {
1251− this$1._disposable = this$1.source.run(this$1)(scheduler);
1252− }
1253− return new MulticastDisposable(this$1, sink)
1254− }
1255−};
1256−
1257−MulticastSource.prototype._dispose = function _dispose () {
1258− var disposable = this._disposable;
1259− this._disposable = emptyDisposable$1;
1260− return Promise.resolve(disposable).then(dispose$1)
1261−};
1262−
1263−MulticastSource.prototype.add = function add (sink) {
1264− this.sinks = append(sink, this.sinks);
1265− return this.sinks.length
1266−};
1267−
1268−MulticastSource.prototype.remove = function remove$1 (sink) {
1269− var i = findIndex(sink, this.sinks);
1270− // istanbul ignore next
1271− if (i >= 0) {
1272− this.sinks = remove(i, this.sinks);
1273− }
1274−
1275− return this.sinks.length
1276−};
1277−
1278−MulticastSource.prototype.event = function event (time) {
1279− var this$1 = this;
1280−
1281− return function (value) {
1282− var s = this$1.sinks;
1283− if (s.length === 1) {
1284− return s[0].event(time)(value)
1285− }
1286− for (var i = 0; i < s.length; ++i) {
1287− s[i].event(time)(value);
1288− }
1289− }
1290−};
1291−
1292−MulticastSource.prototype.end = function end (time) {
1293− var s = this.sinks;
1294− for (var i = 0; i < s.length; ++i) {
1295− s[i].end(time);
1296− }
1297−};
1298−
1299−var HoldSource = (function (MulticastSource$$1) {
1300− function HoldSource (source, bufferSize) {
1301− MulticastSource$$1.call(this, source);
1302− this.bufferSize = bufferSize;
1303− this.has = false;
1304− this.buffer = [];
1305− }
1306−
1307− if ( MulticastSource$$1 ) HoldSource.__proto__ = MulticastSource$$1;
1308− HoldSource.prototype = Object.create( MulticastSource$$1 && MulticastSource$$1.prototype );
1309− HoldSource.prototype.constructor = HoldSource;
1310−
1311− HoldSource.prototype.add = function add (sink, scheduler) {
1312− if (this.has) {
1313− pushEvents(this.buffer, sink, scheduler);
1314− }
1315−
1316− return MulticastSource$$1.prototype.add.call(this, sink, scheduler)
1317− };
1318−
1319− HoldSource.prototype.event = function event (time) {
1320− var this$1 = this;
1321−
1322− return function (value) {
1323− this$1.has = true;
1324− this$1.buffer = dropAndAppend(value, this$1.buffer, this$1.bufferSize);
1325−
1326− return MulticastSource$$1.prototype.event.call(this$1, time)(value)
1327− }
1328− };
1329−
1330− return HoldSource;
1331−}(MulticastSource));
1332−
1333−function pushEvents (buffer, sink, scheduler) {
1334− var length = buffer.length;
1335−
1336− for (var i = 0; i < length; ++i) {
1337− sink.event(scheduler.now(), buffer[i]);
1338− }
1339−}
1340−
1341−function dropAndAppend (value, buffer, bufferSize) {
1342− if (buffer.length === bufferSize) {
1343− return append(value, drop(1, buffer))
1344− }
1345−
1346− return append(value, buffer)
1347−}
1348−
1349−function multicast (stream) {
1350− var source = stream.source;
1351−
1352− return source instanceof MulticastSource
1353− ? stream
1354− : new Stream(new MulticastSource(source))
1355−}
1356−
1357−var hold = function (bufferSize) { return function (stream) { return new Stream(new HoldSource(stream.source, bufferSize)); }; };
1358−
1359−/* */
1360−
1361−exports.createDisposable = createDisposable;
1362−exports.emptyDisposable = emptyDisposable;
1363−exports.disposeAll = disposeAll;
1364−exports.defaultScheduler = defaultScheduler;
1365−exports.scheduleTasks = scheduleTasks;
1366−exports.endTask = endTask;
1367−exports.eventTask = eventTask;
1368−exports.drain = drain;
1369−exports.flatMap = flatMap;
1370−exports.mergeConcurrently = mergeConcurrently;
1371−exports.join = join;
1372−exports.mergeMapConcurrently = mergeMapConcurrently;
1373−exports.combine2 = combine2;
1374−exports.combine3 = combine3;
1375−exports.combine4 = combine4;
1376−exports.combine5 = combine5;
1377−exports.merge = merge;
1378−exports.concat = concat;
1379−exports.continueWith = continueWith;
1380−exports.switch = switchLatest;
1381−exports.skipRepeatsWith = skipRepeatsWith;
1382−exports.delay = delay;
1383−exports.multicast = multicast;
1384−exports.hold = hold;
src/Control/Stream.purs+40−319
@@ -1,337 +1,58 @@
11module Control.Stream
2− ( STREAM
2+ ( module Control.Stream.Stream
3+ , module Control.Stream.Disposable
4+ , module Control.Stream.Scheduler
5+ , module Control.Stream.Sink
6+ , module Control.Stream.Time
7+ ) where
8+
9+import Control.Stream.Stream
10+ (STREAM
311 , EffStream
4− , Stream
5− , Source
6− , EventFn
7− , EndFn
8− , RunFn
9− , Sink
10− , Scheduler
11− , Task
12− , ScheduledTask
13− , Disposable
14− , Time
15− -- Scheduler-related functions and values
16− , defaultScheduler
17− , eventTask
18− , endTask
19− , createDisposable
20− , emptyDisposable
21− , disposeAll
22− , scheduleTasks
23− -- Stream-related helpers
24− , createStream
25− , createSink
12+ , Stream(..)
13+ , just
14+ , empty
15+ , never
16+ , periodic
2617 , createCombinator
2718 , createEventCombinator
2819 , createEndCombinator
29− , getSource
30− , runSource
31− -- Combinators
3220 , drain
3321 , tapEvent
3422 , tapEnd
35− , filter
23+ , fromArray
24+ , take
25+ , skip
26+ , constant
27+ , continueWith
28+ , concat
29+ , startWith
30+ , scan
31+ , loop
32+ , chain
3633 , mergeConcurrently
37− , mergeMapConcurrently
3834 , join
35+ , mergeMapConcurrently
36+ , concatMap
37+ , switch
3938 , combine2
4039 , combine3
4140 , combine4
4241 , combine5
43− , continueWith
44− , concat
42+ , combineArray
4543 , merge
46− , switch
47− , constant
48− , scan
49− , startWith
50− , multicast
51− , hold
52− , skipRepeats
5344 , skipRepeatsWith
54− , delay
45+ , skipRepeats
46+ , until
47+ , since
48+ , during
5549 , sample
56− -- Stream factories
57− , just
58− , fromArray
59− , empty
60− , never
61− , periodic
50+ , debounce
51+ , throttle
52+ , delay
53+ , multicast
6254 )
63− where
64−
65−import Control.Alt ((<|>))
66−import Control.Applicative (class Applicative)
67−import Control.Apply (class Apply)
68−import Control.Bind (class Bind)
69−import Control.Category (id)
70−import Control.Monad (class Monad)
71−import Control.Monad.Eff (Eff, Pure, runPure)
72−import Control.Monad.Eff.Ref (Ref, modifyRef', newRef, readRef)
73−import Control.Monad.Eff.Unsafe (unsafeCoerceEff, unsafePerformEff)
74−import Control.MonadPlus (class Alt, class Plus)
75−import Data.Eq (class Eq, eq)
76−import Data.Function (flip, ($))
77−import Data.Functor (class Functor, map)
78−import Data.Maybe (Maybe(Just, Nothing), fromJust, isJust)
79−import Data.Monoid (class Monoid)
80−import Data.Semigroup (class Semigroup)
81−import Data.Unit (Unit, unit)
82−import Partial.Unsafe (unsafePartial)
83−
84−newtype Time = Time Int
85−
86−foreign import data STREAM :: !
87−
88−type EffStream e a = Eff (stream :: STREAM | e) a
89−
90−newtype Stream a = Stream { source :: Source a }
91−
92−newtype Source a = Source { run :: RunFn a }
93−
94−type RunFn a = Sink a -> Scheduler -> Disposable
95−type EventFn a = Time -> a -> Unit
96−type EndFn = Time -> Unit
97−
98−newtype Sink a = Sink
99− { event :: Time -> a -> Unit
100− , end :: Time -> Unit
101− }
102−
103−newtype Scheduler = Scheduler
104− { now :: Pure Time
105− , asap :: Task -> ScheduledTask
106− , delay :: Number -> Task -> ScheduledTask
107− , periodic :: Number -> Task -> ScheduledTask
108− , schedule :: Number -> Number -> Task -> ScheduledTask
109− , cancel :: ScheduledTask -> Unit
110− , cancelAll :: (ScheduledTask -> Boolean) -> Unit
111− }
112−
113−type Task =
114− { run :: Time -> Unit
115− , dispose :: Pure Unit
116− }
117−
118−type ScheduledTask =
119− { run :: Pure Unit
120− , dispose :: Pure Unit
121− }
122−
123−type Disposable = { dispose :: Pure Unit }
124−
125−instance functorStream :: Functor Stream where
126− map = _map
127−
128−instance applyStream :: Apply Stream where
129− apply = combine2 id
130−
131−instance applicativeStream :: Applicative Stream where
132− pure = just
133−
134−instance bindStream :: Bind Stream where
135− bind = flatMap
136−
137−instance monadStream :: Monad Stream
138−
139−instance semigroupStream :: Semigroup (Stream a) where
140− append = concat
141−
142−instance altStream :: Alt Stream where
143− alt = \s1 s2 -> merge [s1, s2]
144−
145−instance monoidStream :: Monoid (Stream a) where
146− mempty = empty
147−
148−instance plusStream :: Plus Stream where
149− empty = empty
150−
151−-- Stream-related helpers
152−createStream :: forall a. (Sink a -> Scheduler -> Disposable) -> Stream a
153−createStream run = Stream { source: Source { run } }
154−
155−getSource :: forall a. Stream a -> Source a
156−getSource (Stream stream) = stream.source
157−
158−runSource :: forall a. Source a -> Sink a -> Scheduler -> Disposable
159−runSource (Source source) sink scheduler = source.run sink scheduler
160−
161−runStream :: forall a. Stream a -> Sink a -> Scheduler -> Disposable
162−runStream stream sink scheduler = runSource (getSource stream) sink scheduler
163−
164−createSink :: forall a. (Time -> a -> Unit) -> (Time -> Unit) -> Sink a
165−createSink event end = Sink { event, end }
166−
167−createCombinator :: forall a b. (Sink b -> Scheduler -> EventFn a) -> (Sink b -> Scheduler -> EndFn) -> Stream a -> Stream b
168−createCombinator event end stream = createStream runCombinator
169− where
170− runCombinator :: Sink b -> Scheduler -> Disposable
171− runCombinator sink scheduler =
172− runStream stream (createSink (event sink scheduler) (end sink scheduler)) scheduler
173−
174−createEventCombinator :: forall a b. (Sink b -> Scheduler -> EventFn a) -> Stream a -> Stream b
175−createEventCombinator event stream = createCombinator event end stream
176− where
177− end (Sink sink) (Scheduler scheduler) time = sink.end time
178−
179−createEndCombinator :: forall a. (Sink a -> Scheduler -> EndFn) -> Stream a -> Stream a
180−createEndCombinator end stream = createCombinator event end stream
181− where
182− event :: Sink a -> Scheduler -> EventFn a
183− event (Sink sink) scheduler time value = sink.event time value
184−
185−-- Stream factories
186−just :: forall a. a -> Stream a
187−just a = createStream (runJust a)
188−
189−runJust :: forall a. a -> Sink a -> Scheduler -> Disposable
190−runJust a sink scheduler =
191− scheduleTasks [ eventTask a sink, endTask sink ] scheduler
192−
193−empty :: forall a. Stream a
194−empty = createStream \sink scheduler -> scheduleTasks [endTask sink] scheduler
195−
196−never :: forall a. Stream a
197−never = createStream \sink scheduler -> emptyDisposable
198−
199−-- TODO: remove fromArray because it's not a real event stream
200−-- replace with a fromArray combinator
201−fromArray :: forall a. Array a -> Stream a
202−fromArray arr = createStream (runFromArray arr)
203−
204−runFromArray :: forall a. Array a -> Sink a -> Scheduler -> Disposable
205−runFromArray arr sink scheduler = scheduleTasks tasks scheduler
206− where
207− tasks :: Array Task
208− tasks = eventTasks <|> [ (endTask sink) ]
209−
210− eventTasks :: Array Task
211− eventTasks = map (flip eventTask sink) arr
212−
213−periodic :: Number -> Stream Unit
214−periodic period = createStream (runPeriodic period)
215−
216−runPeriodic :: Number -> Sink Unit -> Scheduler -> Disposable
217−runPeriodic period sink (Scheduler scheduler) = { dispose: scheduledTask.dispose }
218− where
219− scheduledTask = scheduler.periodic period (eventTask unit sink)
220−
221−-- combinators
222−_map :: forall a b. (a -> b) -> Stream a -> Stream b
223−_map f stream = createEventCombinator mapEvent stream
224− where
225− mapEvent (Sink sink) (Scheduler scheduler) time value = sink.event time (f value)
226−
227−filter :: forall a. (a -> Boolean) -> Stream a -> Stream a
228−filter predicate stream = createEventCombinator event stream
229− where
230− event (Sink sink) (Scheduler scheduler) time value = if predicate value
231− then sink.event time value
232− else unit
233−
234−tapEvent :: forall e a. (a -> EffStream e Unit) -> Stream a -> Stream a
235−tapEvent f stream = createEventCombinator event stream
236− where
237− event (Sink sink) (Scheduler scheduler) time value = sink.event time value
238− where -- find a better way to perform these side effects
239− x = unsafePerformEff (f value)
240−
241−tapEnd :: forall e a. EffStream e Unit -> Stream a -> Stream a
242−tapEnd f stream = createEndCombinator end stream
243− where
244− end (Sink sink) (Scheduler scheduler) time = sink.end time
245− where -- find a better way to perform these side effects
246− x = unsafePerformEff f
247−
248−startWith :: forall a. a -> Stream a -> Stream a
249−startWith value stream = concat (just value) stream
250−
251−constant :: forall a b. b -> Stream a -> Stream b
252−constant value stream = createEventCombinator constantEvent stream
253− where
254− constantEvent (Sink sink) (Scheduler scheduler) time x = sink.event time value
255−
256−scan :: forall a b. (b -> a -> b) -> b -> Stream a -> Stream b
257−scan f seed stream = startWith seed $ createEventCombinator scanEvent stream
258− where
259− state :: State b
260− state = createState seed
261−
262− scanEvent :: Sink b -> Scheduler -> Time -> a -> Unit
263− scanEvent (Sink sink) (Scheduler scheduler) time value = sink.event time (state.set \acc -> f acc value)
264−
265−skipRepeats :: forall a. (Eq a) => Stream a -> Stream a
266−skipRepeats = skipRepeatsWith eq
267−
268−sample :: forall a b c. (a -> b -> c) -> Stream a -> Stream b -> Stream c
269−sample f sampler stream = createStream $ runSample (createState Nothing)
270− where
271− runSample :: State (Maybe b) -> Sink c -> Scheduler -> Disposable
272− runSample state sink scheduler = disposeAll
273− [ runStream stream (createHoldSink state) scheduler
274− , runStream sampler (createSampleSink state sink f) scheduler
275− ]
276−
277−createHoldSink :: forall a. State (Maybe a) -> Sink a
278−createHoldSink state = createSink event end
279− where
280− event time value = always unit (state.set \_ -> Just value)
281− end time = unit
282−
283−createSampleSink :: forall a b c. State (Maybe b) -> Sink c -> (a -> b -> c) -> Sink a
284−createSampleSink state (Sink sink) f = createSink event end
285− where
286− end time = sink.end time
287− event time value =
288− if isJust (runPure state.get)
289− then sink.event time $ f value (unsafePartial $ fromJust (runPure state.get))
290− else unit
291−
292−always :: forall a b. a -> b -> a
293−always a b = a
294−
295−-- find a better way to perform these side effects
296−type State a = { get:: Pure a, set :: (a -> a) -> a }
297−
298−createState :: forall a. a -> State a
299−createState seed = { set, get }
300− where
301− ref :: Ref a
302− ref = unsafePerformEff $ unsafeCoerceEff $ newRef seed
303−
304− set :: (a -> a) -> a
305− set f = unsafePerformEff $ unsafeCoerceEff $
306− modifyRef' ref \x -> { state: (f x), value: (f x) }
307−
308− get :: Pure a
309− get = unsafeCoerceEff $ readRef ref
310−
311−-- Foreign imports
312−-- Scheduler-related
313−foreign import defaultScheduler :: Scheduler
314−foreign import eventTask :: forall a. a -> Sink a -> Task
315−foreign import endTask :: forall a. Sink a -> Task
316−foreign import createDisposable :: forall a. (a -> Unit) -> a -> Disposable
317−foreign import emptyDisposable :: Disposable
318−foreign import disposeAll :: Array Disposable -> Disposable
319−foreign import scheduleTasks :: Array Task -> Scheduler -> Disposable
320−-- combinators
321−foreign import drain :: forall e a. Stream a -> EffStream e Unit
322−foreign import mergeConcurrently :: forall a. Int -> Stream (Stream a) -> Stream a
323−foreign import mergeMapConcurrently :: forall a b. (a -> Stream b) -> Stream a -> Stream b
324−foreign import combine2 :: forall a b c. (a -> b -> c) -> Stream a -> Stream b -> Stream c
325−foreign import combine3 :: forall a b c d. (a -> b -> c -> d) -> Stream a -> Stream b -> Stream c -> Stream d
326−foreign import combine4 :: forall a b c d e. (a -> b -> c -> d -> e) -> Stream a -> Stream b -> Stream c -> Stream d -> Stream e
327−foreign import combine5 :: forall a b c d e f. (a -> b -> c -> d -> e -> f) -> Stream a -> Stream b -> Stream c -> Stream d -> Stream e -> Stream f
328−foreign import join :: forall a. Stream (Stream a) -> Stream a
329−foreign import flatMap :: forall a b. Stream a -> (a -> Stream b) -> Stream b
330−foreign import continueWith :: forall a. (Unit -> Stream a) -> Stream a -> Stream a
331−foreign import concat :: forall a. Stream a -> Stream a -> Stream a
332−foreign import merge :: forall a. Array (Stream a) -> Stream a
333−foreign import switch :: forall a. Stream (Stream a) -> Stream a
334−foreign import multicast :: forall a. Stream a -> Stream a
335−foreign import hold :: forall a. Int -> Stream a -> Stream a
336−foreign import skipRepeatsWith :: forall a. (a -> a -> Boolean) -> Stream a -> Stream a
337−foreign import delay :: forall a. Number -> Stream a -> Stream a
55+import Control.Stream.Disposable (Disposable, emptyDisposable)
56+import Control.Stream.Scheduler (Scheduler(..), Task, ScheduledTask, scheduler, scheduleTasks, eventTask, endTask)
57+import Control.Stream.Sink (Sink(..))
58+import Control.Stream.Time (Time)
src/Control/Stream/Stream.jsnew file+1244
@@ -0,0 +1,1244 @@
1+var ControlStreamScheduler = require('../Control.Stream.Scheduler')
2+var ControlStreamStream = require('../Control.Stream.Stream')
3+
4+exports.drain = function (stream) { return function () { return runSource(stream, ControlStreamScheduler.scheduler) } }
5+
6+exports.subscribe = function (event) {
7+ return function (end) {
8+ return function (stream) {
9+ return function () {
10+ var disposable = new SettableDisposable()
11+
12+ var observer = { event: event, end: end }
13+
14+ var d = stream.run(observer)(ControlStreamScheduler.scheduler)
15+
16+ disposable.setDisposable(d)
17+
18+ return {}
19+ }
20+ }
21+ }
22+}
23+
24+function runSource (source, scheduler) {
25+ var disposable = new SettableDisposable()
26+
27+ function event (t) {
28+ return function (x) {
29+ return {}
30+ }
31+ }
32+
33+ function end (t) {
34+ disposable.dispose()
35+ return {}
36+ }
37+
38+ var observer = { event: event, end: end }
39+
40+ var d = source.run(observer)(scheduler)
41+
42+ disposable.setDisposable(d)
43+
44+ return {}
45+}
46+
47+var SettableDisposable = function SettableDisposable () {
48+ this.disposable = void 0
49+ this.disposed = false
50+ this.result = void 0
51+}
52+
53+SettableDisposable.prototype.setDisposable = function setDisposable (disposable) {
54+ this.disposable = disposable
55+
56+ if (this.disposed) {
57+ this.result = disposable.dispose()
58+ }
59+}
60+
61+SettableDisposable.prototype.dispose = function dispose () {
62+ if (this.disposed) {
63+ return this.result
64+ }
65+
66+ this.disposed = true
67+
68+ if (this.disposable !== void 0) {
69+ this.result = this.disposable.dispose()
70+ }
71+
72+ return this.result
73+}
74+
75+exports.fromArray = function (array) {
76+ return function (stream) {
77+ return new FromArray(array, stream)
78+ }
79+}
80+
81+function FromArray (array, stream) {
82+ this.array = array
83+ this.stream = stream
84+}
85+
86+FromArray.prototype.run = function (sink) {
87+ var that = this
88+
89+ return function (scheduler) {
90+ return that.stream.run(new FromArraySink(sink, that.array))(scheduler)
91+ }
92+}
93+
94+function FromArraySink (sink, array) {
95+ this.sink = sink
96+ this.array = array
97+}
98+
99+FromArraySink.prototype.event = function (time) {
100+ var array = this.array
101+ var sink = this.sink
102+ return function (x) {
103+ var value = array.shift()
104+
105+ if (value) {
106+ sink.event(time)(value)
107+ } else {
108+ sink.end(time)
109+ }
110+ }
111+}
112+
113+FromArraySink.prototype.end = function (time) {
114+ this.sink.end(time)
115+}
116+
117+var LinkedList = function LinkedList () {
118+ this.head = null
119+ this.length = 0
120+}
121+
122+LinkedList.prototype.add = function add (x) {
123+ if (this.head !== null) {
124+ this.head.prev = x
125+ x.next = this.head
126+ }
127+ this.head = x
128+ ++this.length
129+}
130+
131+LinkedList.prototype.remove = function remove (x) { // eslint-disable-line complexity
132+ --this.length
133+ if (x === this.head) {
134+ this.head = this.head.next
135+ }
136+ if (x.next !== null) {
137+ x.next.prev = x.prev
138+ x.next = null
139+ }
140+ if (x.prev !== null) {
141+ x.prev.next = x.next
142+ x.prev = null
143+ }
144+}
145+
146+LinkedList.prototype.isEmpty = function isEmpty () {
147+ return this.length === 0
148+}
149+
150+LinkedList.prototype.dispose = function dispose () {
151+ if (this.isEmpty()) { return }
152+
153+ var x = this.head
154+ this.head = null
155+ this.length = 0
156+
157+ while (x !== null) {
158+ x.dispose()
159+ x = x.next
160+ }
161+}
162+
163+exports.chain = function (f) {
164+ return function (stream) {
165+ return mergeMapConcurrently(f)(Infinity)(stream)
166+ }
167+}
168+
169+exports.mergeConcurrently = mergeConcurrently
170+
171+function mergeConcurrently (concurrency) {
172+ return function (stream) {
173+ return mergeMapConcurrently(function (x) { return x })(concurrency)(stream)
174+ }
175+}
176+
177+exports.join = mergeConcurrently(Infinity)
178+
179+exports.mergeMapConcurrently = mergeMapConcurrently
180+
181+function mergeMapConcurrently (f) {
182+ return function (concurrency) {
183+ return function (stream) {
184+ var mstream = new MergeConcurrently(f, concurrency, stream)
185+
186+ mstream.run.bind(mstream)
187+
188+ return mstream
189+ }
190+ }
191+}
192+
193+function MergeConcurrently (f, concurrency, source) {
194+ this.f = f
195+ this.concurrency = concurrency
196+ this.source = source
197+}
198+
199+MergeConcurrently.prototype.run = function run (sink) {
200+ var this$1 = this
201+
202+ return function (scheduler) { return new Outer(this$1.f, this$1.concurrency, this$1.source, sink, scheduler) }
203+}
204+
205+var Outer = function Outer (f, concurrency, source, sink, scheduler) {
206+ this.f = f
207+ this.concurrency = concurrency
208+ this.sink = sink
209+ this.scheduler = scheduler
210+ this.pending = []
211+ this.current = new LinkedList()
212+
213+ var disposed = false
214+ var disposable = source.run(this)(scheduler)
215+
216+ this.disposable = {
217+ dispose: function () {
218+ if (disposed) return
219+
220+ disposed = true
221+ disposable.dispose()
222+ }
223+ }
224+
225+ this.active = true
226+}
227+
228+Outer.prototype.event = function event (t) {
229+ var this$1 = this
230+
231+ return function (x) { return this$1._addInner(t, x) }
232+}
233+
234+Outer.prototype._addInner = function _addInner (t, x) {
235+ if (this.current.length < this.concurrency) {
236+ this._startInner(t, x)
237+ } else {
238+ this.pending.push(x)
239+ }
240+}
241+
242+Outer.prototype._startInner = function _startInner (t, x) {
243+ this._initInner(t, x)
244+}
245+
246+Outer.prototype._initInner = function _initInner (t, x) {
247+ var innerSink = new Inner(t, this, this.sink)
248+ innerSink.disposable = mapAndRun(this.f, x, innerSink, this.scheduler)
249+ this.current.add(innerSink)
250+}
251+
252+Outer.prototype.end = function end (t) {
253+ this.active = false
254+ this.disposable.dispose()
255+ this._checkEnd(t)
256+}
257+
258+Outer.prototype.dispose = function dispose () {
259+ this.active = false
260+ this.pending.length = 0
261+ this.disposable.dispose()
262+ this.current.dispose()
263+}
264+
265+Outer.prototype._endInner = function _endInner (t, inner) {
266+ this.current.remove(inner)
267+ inner.dispose()
268+
269+ if (this.pending.length === 0) {
270+ this._checkEnd(t)
271+ } else {
272+ this._startInner(t, this.pending.shift())
273+ }
274+}
275+
276+Outer.prototype._checkEnd = function _checkEnd (t) {
277+ if (!this.active && this.current.isEmpty()) {
278+ this.sink.end(t)
279+ }
280+}
281+
282+var mapAndRun = function (f, x, sink, scheduler) { return f(x).run(sink)(scheduler) }
283+
284+var Inner = function Inner (time, outer, sink) {
285+ this.prev = this.next = null
286+ this.time = time
287+ this.outer = outer
288+ this.sink = sink
289+ this.disposable = void 0
290+}
291+
292+Inner.prototype.event = function event (t) {
293+ var this$1 = this
294+
295+ return function (x) { return this$1.sink.event(Math.max(t, this$1.time))(x) }
296+}
297+
298+Inner.prototype.end = function end (t) {
299+ this.outer._endInner(Math.max(t, this.time), this)
300+}
301+
302+Inner.prototype.dispose = function dispose () {
303+ return this.disposable.dispose()
304+}
305+
306+exports.switch = function switchLatest (stream) {
307+ return new Switch(stream)
308+}
309+
310+function Switch (source) {
311+ this.source = source
312+}
313+
314+Switch.prototype.run = function (sink) {
315+ var this$1 = this
316+
317+ return function (scheduler) {
318+ var switchSink = new SwitchSink(sink, scheduler)
319+ var disposable = this$1.source.run(switchSink)(scheduler)
320+
321+ return {
322+ dispose: function () {
323+ switchSink.dispose()
324+ disposable.dispose()
325+ }
326+ }
327+ }
328+}
329+
330+function SwitchSink (sink, scheduler) {
331+ this.sink = sink
332+ this.scheduler = scheduler
333+ this.current = null
334+ this.ended = false
335+}
336+
337+SwitchSink.prototype.event = function (t) {
338+ var this$1 = this
339+
340+ return function (stream) {
341+ this$1._disposeCurrent(t) // TODO: capture the result of this dispose
342+ this$1.current = new Segment(t, Infinity, this$1, this$1.sink)
343+ this$1.current.disposable = stream.run(this$1.current)(this$1.scheduler)
344+ }
345+}
346+
347+SwitchSink.prototype.end = function (t, x) {
348+ this.ended = true
349+ this._checkEnd(t, x)
350+}
351+
352+SwitchSink.prototype.dispose = function () {
353+ return this._disposeCurrent(this.scheduler.now())
354+}
355+
356+SwitchSink.prototype._disposeCurrent = function (t) {
357+ if (this.current !== null) {
358+ return this.current._dispose(t)
359+ }
360+}
361+
362+SwitchSink.prototype._disposeInner = function (t, inner) {
363+ inner._dispose(t) // TODO: capture the result of this dispose
364+ if (inner === this.current) {
365+ this.current = null
366+ }
367+}
368+
369+SwitchSink.prototype._checkEnd = function (t, x) {
370+ if (this.ended && this.current === null) {
371+ this.sink.end(t, x)
372+ }
373+}
374+
375+SwitchSink.prototype._endInner = function (t, x, inner) {
376+ this._disposeInner(t, inner)
377+ this._checkEnd(t, x)
378+}
379+
380+function Segment (min, max, outer, sink) {
381+ this.min = min
382+ this.max = max
383+ this.outer = outer
384+ this.sink = sink
385+ this.disposable = { dispose: Function.prototype }
386+}
387+
388+Segment.prototype.event = function (t) {
389+ var this$1 = this
390+
391+ return function (x) {
392+ if (t < this$1.max) {
393+ this$1.sink.event(Math.max(t, this$1.min))(x)
394+ }
395+ }
396+}
397+
398+Segment.prototype.end = function (t, x) {
399+ this.outer._endInner(Math.max(t, this.min), x, this)
400+}
401+
402+Segment.prototype._dispose = function (t) {
403+ this.max = t
404+ this.disposable.dispose()
405+}
406+
407+exports.continueWith = function continueWith (f) {
408+ return function (stream) {
409+ return new ContinueWith(f, stream)
410+ }
411+}
412+
413+function ContinueWith (f, source) {
414+ this.f = f
415+ this.source = source
416+}
417+
418+ContinueWith.prototype.run = function (sink) {
419+ var that = this
420+ return function (scheduler) {
421+ return new ContinueWithSink(that.f, that.source, sink, scheduler)
422+ }
423+}
424+
425+function ContinueWithSink (f, source, sink, scheduler) {
426+ this.f = f
427+ this.sink = sink
428+ this.scheduler = scheduler
429+ this.active = true
430+ this.disposable = source.run(this)(scheduler)
431+}
432+
433+ContinueWithSink.prototype.event = function (t) {
434+ var that = this
435+
436+ return function (x) {
437+ if (!that.active) {
438+ return
439+ }
440+ that.sink.event(t)(x)
441+ }
442+}
443+
444+ContinueWithSink.prototype.end = function (t, x) {
445+ if (!this.active) {
446+ return
447+ }
448+
449+ this.disposable.dispose()
450+
451+ this._startNext(t, x, this.sink)
452+}
453+
454+ContinueWithSink.prototype._startNext = function (t, x, sink) {
455+ try {
456+ this.disposable = this._continue(this.f, x, sink)
457+ } catch (e) {
458+ sink.error(t, e)
459+ }
460+}
461+
462+ContinueWithSink.prototype._continue = function (f, x, sink) {
463+ return f(x).run(sink)(this.scheduler)
464+}
465+
466+ContinueWithSink.prototype.dispose = function () {
467+ this.active = false
468+ return this.disposable.dispose()
469+}
470+
471+exports.combine2 = function (f) { return function (s1) { return function (s2) { return combineArray(f, [s1, s2]) } } }
472+exports.combine3 = function (f) { return function (s1) { return function (s2) { return function (s3) { return combineArray(f, [s1, s2, s3]) } } } }
473+exports.combine4 = function (f) { return function (s1) { return function (s2) { return function (s3) { return function (s4) { return combineArray(f, [s1, s2, s3, s4]) } } } } }
474+exports.combine5 = function (f) { return function (s1) { return function (s2) { return function (s3) { return function (s4) { return function (s5) { return combineArray(f, [s1, s2, s3, s4, s5]) } } } } } }
475+
476+exports.combineArray = function (streams) {
477+ return combineArray(Array, streams)
478+}
479+
480+function combineArray (f, streams) {
481+ return new Combine(f, streams)
482+}
483+
484+function Combine (f, sources) {
485+ this.f = f
486+ this.sources = sources
487+}
488+
489+Combine.prototype.run = function (sink) {
490+ var this$1 = this
491+
492+ return function (scheduler) {
493+ var l = this$1.sources.length
494+ var disposables = new Array(l)
495+ var sinks = new Array(l)
496+
497+ var mergeSink = new CombineSink(disposables, sinks, sink, this$1.f)
498+
499+ for (var indexSink, i = 0; i < l; ++i) {
500+ indexSink = sinks[i] = new IndexSink(i, mergeSink)
501+ disposables[i] = this$1.sources[i].run(indexSink)(scheduler)
502+ }
503+
504+ return {
505+ dispose: function () {
506+ disposables.forEach(function (d) {
507+ d.dispose()
508+ })
509+ }
510+ }
511+ }
512+}
513+
514+function CombineSink (disposables, sinks, sink, f) {
515+ var this$1 = this
516+
517+ this.sink = sink
518+ this.disposables = disposables
519+ this.sinks = sinks
520+ this.f = f
521+
522+ var l = sinks.length
523+ this.awaiting = l
524+ this.values = new Array(l)
525+ this.hasValue = new Array(l)
526+
527+ for (var i = 0; i < l; ++i) {
528+ this$1.hasValue[i] = false
529+ }
530+
531+ this.activeCount = sinks.length
532+}
533+
534+CombineSink.prototype.event = function (t) {
535+ var this$1 = this
536+
537+ return function (indexedValue) {
538+ var i = indexedValue.index
539+ var awaiting = this$1._updateReady(i)
540+
541+ this$1.values[i] = indexedValue.value
542+ if (awaiting === 0) {
543+ var value = tail(this$1.values).reduce(function (f, x) { return f(x) }, this$1.f(this$1.values[0]))
544+ this$1.sink.event(t)(value)
545+ }
546+ }
547+}
548+
549+CombineSink.prototype._updateReady = function (index) {
550+ if (this.awaiting > 0) {
551+ if (!this.hasValue[index]) {
552+ this.hasValue[index] = true
553+ this.awaiting -= 1
554+ }
555+ }
556+ return this.awaiting
557+}
558+
559+CombineSink.prototype.end = function (t, indexedValue) {
560+ this.disposables[indexedValue.index].dispose()
561+
562+ if (--this.activeCount === 0) {
563+ this.sink.end(t)
564+ }
565+}
566+
567+var IndexSink = function IndexSink (i, sink) {
568+ this.sink = sink
569+ this.index = i
570+ this.active = true
571+ this.value = void 0
572+}
573+
574+IndexSink.prototype.event = function event (t) {
575+ var this$1 = this
576+
577+ return function (x) {
578+ if (!this$1.active) { return }
579+
580+ this$1.value = x
581+ this$1.sink.event(t)(this$1)
582+ }
583+}
584+
585+IndexSink.prototype.end = function end (t) {
586+ if (!this.active) { return }
587+
588+ this.active = false
589+ this.sink.end(t, { index: this.index })
590+}
591+
592+function drop (n, a) { // eslint-disable-line complexity
593+ if (n < 0) {
594+ throw new TypeError('n must be >= 0')
595+ }
596+
597+ var l = a.length
598+ if (n === 0 || l === 0) {
599+ return a
600+ }
601+
602+ if (n >= l) {
603+ return []
604+ }
605+
606+ return unsafeDrop(n, a, l - n)
607+}
608+
609+// unsafeDrop :: Int -> [a] -> Int -> [a]
610+// Internal helper for drop
611+function unsafeDrop (n, a, l) {
612+ var b = new Array(l)
613+ for (var i = 0; i < l; ++i) {
614+ b[i] = a[n + i]
615+ }
616+ return b
617+}
618+
619+// tail :: [a] -> [a]
620+// drop head element
621+function tail (a) {
622+ return drop(1, a)
623+}
624+
625+exports.merge = function merge (streams) {
626+ return mergeSources(streams)
627+}
628+
629+/**
630+ * This implements fusion/flattening for merge. It will
631+ * fuse adjacent merge operations. For example:
632+ * - a.merge(b).merge(c) effectively becomes merge(a, b, c)
633+ * - merge(a, merge(b, c)) effectively becomes merge(a, b, c)
634+ * It does this by concatenating the sources arrays of
635+ * any nested Merge sources, in effect "flattening" nested
636+ * merge operations into a single merge.
637+ */
638+function mergeSources (streams) {
639+ return new Merge(reduce(appendSources, [], streams))
640+}
641+
642+function appendSources (sources, stream) {
643+ return stream instanceof Merge
644+ ? sources.concat(stream.sources)
645+ : sources.concat(stream)
646+}
647+
648+function Merge (sources) {
649+ this.sources = sources
650+}
651+
652+Merge.prototype.run = function (sink) {
653+ var this$1 = this
654+
655+ return function (scheduler) {
656+ var l = this.sources.length
657+ var disposables = new Array(l)
658+ var sinks = new Array(l)
659+
660+ var mergeSink = new MergeSink(disposables, sinks, sink)
661+
662+ for (var indexSink, i = 0; i < l; ++i) {
663+ indexSink = sinks[i] = new IndexSink(i, mergeSink)
664+ disposables[i] = this$1.sources[i].run(indexSink, scheduler)
665+ }
666+
667+ return all(disposables)
668+ }
669+}
670+
671+function all (disposables) {
672+ return {
673+ dispose: function () {
674+ disposables.forEach(function (d) {
675+ d.dispose()
676+ })
677+ }
678+ }
679+}
680+
681+function MergeSink (disposables, sinks, sink) {
682+ this.sink = sink
683+ this.disposables = disposables
684+ this.activeCount = sinks.length
685+}
686+
687+MergeSink.prototype.event = function (t) {
688+ var that = this
689+ return function (indexValue) {
690+ that.sink.event(t)(indexValue.value)
691+ }
692+}
693+
694+MergeSink.prototype.end = function (t, indexedValue) {
695+ this.disposables[indexedValue.index].dispose()
696+ if (--this.activeCount === 0) {
697+ this.sink.end(t, indexedValue.value)
698+ }
699+}
700+
701+function reduce (f, z, a) {
702+ var r = z
703+ for (var i = 0, l = a.length; i < l; ++i) {
704+ r = f(r, a[i], i)
705+ }
706+ return r
707+}
708+
709+exports.until = function takeUntil (signal) {
710+ return function (stream) {
711+ return new Until(signal, stream)
712+ }
713+}
714+
715+exports.since = function skipUntil (signal) {
716+ return function (stream) {
717+ return new Since(signal, stream)
718+ }
719+}
720+
721+function Until (maxSignal, source) {
722+ this.maxSignal = maxSignal
723+ this.source = source
724+}
725+
726+Until.prototype.run = function (sink) {
727+ var that = this
728+ return function (scheduler) {
729+ var min = new Bound(-Infinity, sink)
730+ var max = new UpperBound(that.maxSignal, sink, scheduler)
731+ var disposable = that.source.run(new TimeWindowSink(min, max, sink), scheduler)
732+
733+ return all([min, max, disposable])
734+ }
735+}
736+
737+function Since (minSignal, source) {
738+ this.minSignal = minSignal
739+ this.source = source
740+}
741+
742+Since.prototype.run = function (sink) {
743+ var that = this
744+ return function (scheduler) {
745+ var min = new LowerBound(that.minSignal, sink, scheduler)
746+ var max = new Bound(Infinity, sink)
747+ var disposable = that.source.run(new TimeWindowSink(min, max, sink))(scheduler)
748+
749+ return all([min, max, disposable])
750+ }
751+}
752+
753+function Bound (value, sink) {
754+ this.value = value
755+ this.sink = sink
756+}
757+
758+Bound.prototype.event = noop
759+Bound.prototype.end = noop
760+Bound.prototype.dispose = noop
761+
762+function TimeWindowSink (min, max, sink) {
763+ this.min = min
764+ this.max = max
765+ this.sink = sink
766+}
767+
768+TimeWindowSink.prototype.event = function (t, x) {
769+ var that = this
770+ return function (x) {
771+ if (t >= that.min.value && t < that.max.value) {
772+ that.sink.event(t)(x)
773+ }
774+ }
775+}
776+
777+TimeWindowSink.prototype.end = function (time) {
778+ this.sink.end(time)
779+}
780+
781+function LowerBound (signal, sink, scheduler) {
782+ this.value = Infinity
783+ this.sink = sink
784+ this.disposable = signal.run(this)(scheduler)
785+}
786+
787+LowerBound.prototype.event = function (t /*, x */) {
788+ return function () {
789+ if (t < this.value) {
790+ this.value = t
791+ }
792+ }
793+}
794+
795+LowerBound.prototype.end = noop
796+
797+LowerBound.prototype.dispose = function () {
798+ return this.disposable.dispose()
799+}
800+
801+function UpperBound (signal, sink, scheduler) {
802+ this.value = Infinity
803+ this.sink = sink
804+ this.disposable = signal.run(this)(scheduler)
805+}
806+
807+UpperBound.prototype.event = function (t) {
808+ var that = this
809+
810+ return function (x) {
811+ if (t < this.value) {
812+ that.value = t
813+ that.sink.end(t)
814+ }
815+ }
816+}
817+
818+UpperBound.prototype.end = noop
819+
820+UpperBound.prototype.dispose = function () {
821+ return this.disposable.dispose()
822+}
823+
824+function noop () { }
825+
826+exports.throttle = function throttle (period) {
827+ return function (stream) {
828+ return throttleSource(period, stream)
829+ }
830+}
831+
832+function throttleSource (period, source) {
833+ return source instanceof Throttle
834+ ? fuseThrottle(period, source)
835+ : new Throttle(period, source)
836+}
837+
838+function fuseThrottle (period, source) {
839+ return new Throttle(Math.max(period, source.period), source.source)
840+}
841+
842+function Throttle (period, source) {
843+ this.period = period
844+ this.source = source
845+}
846+
847+Throttle.prototype.run = function (sink) {
848+ var that = this
849+ return function (scheduler) {
850+ return that.source.run(new ThrottleSink(that.period, sink), scheduler)
851+ }
852+}
853+
854+function ThrottleSink (period, sink) {
855+ this.time = 0
856+ this.period = period
857+ this.sink = sink
858+}
859+
860+ThrottleSink.prototype.event = function (t) {
861+ var that = this
862+ return function (x) {
863+ if (t >= this.time) {
864+ that.time = t + this.period
865+ that.sink.event(t)(x)
866+ }
867+ }
868+}
869+
870+ThrottleSink.prototype.end = function (t) { this.sink.end(t) }
871+
872+exports.debounce = function debounce (period) {
873+ return function (stream) {
874+ return new Debounce(period, stream)
875+ }
876+}
877+
878+function Debounce (dt, source) {
879+ this.dt = dt
880+ this.source = source
881+}
882+
883+Debounce.prototype.run = function (sink) {
884+ var that = this
885+ return function (scheduler) {
886+ return new DebounceSink(that.dt, that.source, sink, scheduler)
887+ }
888+}
889+
890+function DebounceSink (dt, source, sink, scheduler) {
891+ this.dt = dt
892+ this.sink = sink
893+ this.scheduler = scheduler
894+ this.value = void 0
895+ this.timer = null
896+
897+ var sourceDisposable = source.run(this)(scheduler)
898+ this.disposable = all([this, sourceDisposable])
899+}
900+
901+DebounceSink.prototype.event = function (t) {
902+ var that = this
903+ return function (x) {
904+ that._clearTimer()
905+ that.value = x
906+ var task = ControlStreamScheduler.eventTask(that.sink)(x)
907+ that.timer = this.scheduler.delay(this.dt, task)
908+ }
909+}
910+
911+DebounceSink.prototype.end = function (t, x) {
912+ if (this._clearTimer()) {
913+ this.sink.event(t, this.value)
914+ this.value = void 0
915+ }
916+ this.sink.end(t, x)
917+}
918+
919+DebounceSink.prototype.dispose = function () {
920+ this._clearTimer()
921+}
922+
923+DebounceSink.prototype._clearTimer = function () {
924+ if (this.timer === null) {
925+ return false
926+ }
927+ this.timer.dispose()
928+ this.timer = null
929+ return true
930+}
931+
932+exports.delay = function delay (delayTime) {
933+ return function (stream) {
934+ return delayTime <= 0 ? stream : new Delay(delayTime, stream)
935+ }
936+}
937+
938+function Delay (dt, source) {
939+ this.dt = dt
940+ this.source = source
941+}
942+
943+Delay.prototype.run = function (sink) {
944+ var that = this
945+
946+ return function (scheduler) {
947+ var delaySink = new DelaySink(that.dt, sink, scheduler)
948+ return all([delaySink, that.source.run(delaySink)(scheduler)])
949+ }
950+}
951+
952+function DelaySink (dt, sink, scheduler) {
953+ this.dt = dt
954+ this.sink = sink
955+ this.scheduler = scheduler
956+}
957+
958+DelaySink.prototype.dispose = function () {
959+ var self = this
960+ this.scheduler.cancelAll(function (task) {
961+ return task.sink === self.sink
962+ })
963+}
964+
965+DelaySink.prototype.event = function (t) {
966+ var that = this
967+ return function (x) {
968+ that.scheduler.delay(that.dt)(ControlStreamScheduler.eventTask(that.sink)(x))
969+ }
970+}
971+
972+DelaySink.prototype.end = function (t, x) {
973+ this.scheduler.delay(this.dt)(ControlStreamScheduler.endTask(this.sink))
974+}
975+
976+var MulticastDisposable = function MulticastDisposable (source, sink) {
977+ this.source = source
978+ this.sink = sink
979+ this.disposed = false
980+}
981+
982+MulticastDisposable.prototype.dispose = function dispose () {
983+ if (this.disposed) {
984+ return
985+ }
986+ this.disposed = true
987+ var remaining = this.source.remove(this.sink)
988+ return remaining === 0 && this.source._dispose()
989+}
990+
991+var dispose = function (disposable) { return disposable.dispose() }
992+
993+var emptyDisposable = {
994+ dispose: function dispose$1 () { }
995+}
996+
997+function MulticastSource (source) {
998+ this.source = source
999+ this.sinks = []
1000+ this._disposable = emptyDisposable
1001+}
1002+
1003+MulticastSource.prototype.run = function run (sink) {
1004+ var that = this
1005+ return function (scheduler) {
1006+ var n = that.add(sink)
1007+ if (n === 1) {
1008+ that._disposable = that.source.run(that)(scheduler)
1009+ }
1010+ return new MulticastDisposable(that, sink)
1011+ }
1012+}
1013+
1014+MulticastSource.prototype._dispose = function _dispose () {
1015+ var disposable = this._disposable
1016+ this._disposable = emptyDisposable
1017+ return Promise.resolve(disposable).then(dispose)
1018+}
1019+
1020+MulticastSource.prototype.add = function add (sink) {
1021+ this.sinks = append(sink, this.sinks)
1022+ return this.sinks.length
1023+}
1024+
1025+function append (x, a) {
1026+ var l = a.length
1027+ var b = new Array(l + 1)
1028+ for (var i = 0; i < l; ++i) {
1029+ b[i] = a[i]
1030+ }
1031+
1032+ b[l] = x
1033+ return b
1034+}
1035+
1036+MulticastSource.prototype.remove = function remove$1 (sink) {
1037+ var i = findIndex(sink, this.sinks)
1038+ // istanbul ignore next
1039+ if (i >= 0) {
1040+ this.sinks = remove(i, this.sinks)
1041+ }
1042+
1043+ return this.sinks.length
1044+}
1045+
1046+function findIndex (x, a) {
1047+ for (var i = 0, l = a.length; i < l; ++i) {
1048+ if (x === a[i]) {
1049+ return i
1050+ }
1051+ }
1052+ return -1
1053+}
1054+
1055+function remove (i, a) { // eslint-disable-line complexity
1056+ if (i < 0) {
1057+ throw new TypeError('i must be >= 0')
1058+ }
1059+
1060+ var l = a.length
1061+ if (l === 0 || i >= l) { // exit early if index beyond end of array
1062+ return a
1063+ }
1064+
1065+ if (l === 1) { // exit early if index in bounds and length === 1
1066+ return []
1067+ }
1068+
1069+ return unsafeRemove(i, a, l - 1)
1070+}
1071+
1072+// unsafeRemove :: Int -> [a] -> Int -> [a]
1073+// Internal helper to remove element at index
1074+function unsafeRemove (i, a, l) {
1075+ var b = new Array(l)
1076+ var j
1077+ for (j = 0; j < i; ++j) {
1078+ b[j] = a[j]
1079+ }
1080+ for (j = i; j < l; ++j) {
1081+ b[j] = a[j + 1]
1082+ }
1083+
1084+ return b
1085+}
1086+
1087+MulticastSource.prototype.event = function event (time) {
1088+ var that = this
1089+ return function (value) {
1090+ var s = that.sinks
1091+ if (s.length === 1) {
1092+ return s[0].event(time)(value)
1093+ }
1094+ for (var i = 0; i < s.length; ++i) {
1095+ s[i].event(time)(value)
1096+ }
1097+ }
1098+}
1099+
1100+MulticastSource.prototype.end = function end (time) {
1101+ var s = this.sinks
1102+ for (var i = 0; i < s.length; ++i) {
1103+ s[i].end(time)
1104+ }
1105+}
1106+
1107+MulticastSource.prototype.until = function (signal) {
1108+ return exports.until(signal)(this)
1109+}
1110+
1111+exports.multicast = function multicast (stream) {
1112+ return new MulticastSource(stream)
1113+}
1114+
1115+function __extends (d, b) { // eslint-disable-line complexity
1116+ for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]
1117+ function __ () { this.constructor = d }
1118+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __())
1119+}
1120+
1121+var HoldSubjectSource = (function (_super) {
1122+ __extends(HoldSubjectSource, _super)
1123+ function HoldSubjectSource (source, bufferSize) {
1124+ var _this = _super.call(this, source) || this
1125+ _this.has = false
1126+ _this.buffer = []
1127+ _this.bufferSize = bufferSize
1128+ return _this
1129+ }
1130+ HoldSubjectSource.prototype.add = function (sink) {
1131+ if (this.has) {
1132+ pushEvents(this.buffer, sink)
1133+ }
1134+ return _super.prototype.add.call(this, sink)
1135+ }
1136+ HoldSubjectSource.prototype.event = function (time) {
1137+ var that = this
1138+ return function (value) {
1139+ that.has = true
1140+ that.buffer = dropAndAppend(value, that.buffer, that.bufferSize)
1141+ return _super.prototype.event.call(that, time)(value)
1142+ }
1143+ }
1144+ return HoldSubjectSource
1145+}(MulticastSource))
1146+
1147+function pushEvents (buffer, sink) {
1148+ var length = buffer.length
1149+ for (var i = 0; i < length; ++i) {
1150+ sink.event(ControlStreamScheduler.scheduler.now(), buffer[i])
1151+ }
1152+}
1153+
1154+function dropAndAppend (value, buffer, bufferSize) {
1155+ if (buffer.length === bufferSize) {
1156+ return append(value, drop(1, buffer))
1157+ }
1158+ return append(value, buffer)
1159+}
1160+
1161+exports.hold = function hold (bufferSize) {
1162+ return function (stream) {
1163+ return new HoldSubjectSource(stream, bufferSize)
1164+ }
1165+}
1166+
1167+exports.toSubject = function (stream) {
1168+ return exports.multicast(stream)
1169+}
1170+
1171+exports.toHoldSubject = exports.hold
1172+
1173+exports.fromSubject = function (subject) {
1174+ return subject
1175+}
1176+
1177+exports.subjectEvent = function (value) {
1178+ return function (subject) {
1179+ ControlStreamScheduler.eventTask(subject)(value)
1180+ return subject
1181+ }
1182+}
1183+
1184+exports.subjectEnd = function (value) {
1185+ return function (subject) {
1186+ subject.end(Date.now())
1187+ return subject
1188+ }
1189+}
1190+
1191+exports.proxy = function (value) {
1192+ var stream = new ProxyStream(value)
1193+
1194+ function attach (original) {
1195+ stream.attach(original)
1196+ return original
1197+ }
1198+
1199+ return { attach: attach, stream: stream }
1200+}
1201+
1202+var ProxyStream = (function (_super) {
1203+ __extends(ProxyStream, _super)
1204+ function ProxyStream (value) {
1205+ var _this = _super.call(this, ControlStreamStream.never, 1) || this
1206+ _this.attached = false
1207+ _this.running = false
1208+ _this.buffer.push(value)
1209+ return _this
1210+ }
1211+ ProxyStream.prototype.run = function (sink) {
1212+ var that = this
1213+
1214+ that.add(sink)
1215+
1216+ return function (scheduler) {
1217+ if (that.attached && !that.running) {
1218+ that.running = true
1219+ that._disposable = that.source.run(that)(scheduler)
1220+ return that._disposable
1221+ }
1222+
1223+ return new MulticastDisposable(that, sink)
1224+ }
1225+ }
1226+
1227+ ProxyStream.prototype.attach = function (source) {
1228+ if (this.attached) throw new Error('Can only proxy 1 stream')
1229+ this.attached = true
1230+
1231+ this.source = source
1232+
1233+ if (this.sinks.length) { this._disposable = source.run(this)(ControlStreamScheduler.scheduler) }
1234+ }
1235+
1236+ ProxyStream.prototype.end = function end (time) {
1237+ this.attached = false
1238+ this.running = false
1239+
1240+ return _super.prototype.end.call(this, time)
1241+ }
1242+
1243+ return ProxyStream
1244+}(HoldSubjectSource))
src/Control/Stream/Stream.pursnew file+339
@@ -0,0 +1,339 @@
1+module Control.Stream.Stream
2+ ( STREAM
3+ , EffStream
4+ , Stream(..)
5+ , just
6+ , empty
7+ , never
8+ , periodic
9+ , createCombinator
10+ , createEventCombinator
11+ , createEndCombinator
12+ , drain
13+ , tapEvent
14+ , tapEnd
15+ , fromArray
16+ , take
17+ , skip
18+ , constant
19+ , continueWith
20+ , concat
21+ , startWith
22+ , scan
23+ , loop
24+ , chain
25+ , mergeConcurrently
26+ , join
27+ , mergeMapConcurrently
28+ , concatMap
29+ , switch
30+ , combine2
31+ , combine3
32+ , combine4
33+ , combine5
34+ , combineArray
35+ , merge
36+ , skipRepeatsWith
37+ , skipRepeats
38+ , until
39+ , since
40+ , during
41+ , sample
42+ , debounce
43+ , throttle
44+ , delay
45+ , multicast
46+ , hold
47+ , Subject
48+ , toSubject
49+ , toHoldSubject
50+ , fromSubject
51+ , subjectEvent
52+ , subjectEnd
53+ , subscribe
54+ ) where
55+
56+import Control.Alternative (class Apply)
57+import Control.Bind (class Bind)
58+import Control.Category (id)
59+import Control.Monad.Eff (Eff, Pure, runPure)
60+import Control.Monad.Eff.Ref (Ref, modifyRef', newRef, readRef)
61+import Control.Monad.Eff.Unsafe (unsafeCoerceEff, unsafePerformEff)
62+import Control.MonadPlus (class Alt, class Alternative, class MonadPlus, class Plus)
63+import Control.MonadZero (class Applicative, class MonadZero)
64+import Control.Stream.Disposable (Disposable, emptyDisposable, disposeAll)
65+import Control.Stream.Scheduler (Scheduler(..), endTask, eventTask, scheduleTasks)
66+import Control.Stream.Sink (Sink(..))
67+import Control.Stream.Stream (Stream(..), EffStream, empty, just)
68+import Control.Stream.Time (Time)
69+import Data.Eq ((==))
70+import Data.EuclideanRing ((-))
71+import Data.Functor (class Functor)
72+import Data.Maybe (Maybe(..), fromJust, isNothing)
73+import Data.Monoid (class Monoid)
74+import Data.Unit (Unit, unit)
75+import Partial.Unsafe (unsafePartial)
76+import Prelude (class Eq, class Monad, class Semigroup, eq)
77+
78+foreign import data STREAM :: !
79+
80+type EffStream e a = Eff (stream :: STREAM | e) a
81+
82+foreign import data Subject :: * -> *
83+
84+newtype Stream a = Stream { run :: Sink a -> Scheduler -> Disposable }
85+
86+instance functorStream :: Functor Stream where
87+ map = mapStream
88+
89+instance applyStream :: Apply Stream where
90+ apply = combine2 id
91+
92+instance applicativeStream :: Applicative Stream where
93+ pure = just
94+
95+instance bindStream :: Bind Stream where
96+ bind = chain
97+
98+instance monadStream :: Monad Stream
99+
100+instance semigroupStream :: Semigroup (Stream a) where
101+ append = concat
102+
103+instance altStream :: Alt Stream where
104+ alt = \s1 s2 -> merge [s1, s2]
105+
106+instance monoidStream :: Monoid (Stream a) where
107+ mempty = empty
108+
109+instance plusStream :: Plus Stream where
110+ empty = empty
111+
112+instance alternativeStream :: Alternative Stream
113+
114+instance monadZeroStream :: MonadZero Stream
115+
116+instance monadPlusStream :: MonadPlus Stream
117+
118+just :: forall a. a -> Stream a
119+just a = Stream { run } where
120+ run :: Sink a -> Scheduler -> Disposable
121+ run sink (Scheduler scheduler) =
122+ scheduleTasks scheduler.asap [ (eventTask sink a), (endTask sink) ]
123+
124+empty :: forall a. Stream a
125+empty = Stream { run } where
126+ run :: Sink a -> Scheduler -> Disposable
127+ run sink (Scheduler scheduler) = scheduleTasks scheduler.asap [ endTask sink ]
128+
129+never :: forall a. Stream a
130+never = Stream { run: \sink scheduler -> emptyDisposable }
131+
132+periodic :: Number -> Stream Unit
133+periodic period = Stream { run } where
134+ run :: Sink Unit -> Scheduler -> Disposable
135+ run sink (Scheduler scheduler) = do
136+ let task = eventTask sink unit
137+ let scheduledTask = scheduler.periodic period task
138+ { dispose : scheduledTask.dispose }
139+
140+createCombinator :: forall a b. (Sink b -> Time -> a -> Unit) -> (Sink b -> Time -> Unit) -> Stream a -> Stream b
141+createCombinator event end (Stream stream) = Stream { run } where
142+ run :: Sink b -> Scheduler -> Disposable
143+ run sink scheduler = stream.run (Sink { event: (event sink), end: (end sink) }) scheduler
144+
145+createEventCombinator :: forall a b. (Sink b -> Time -> a -> Unit) -> Stream a -> Stream b
146+createEventCombinator event stream = createCombinator event end stream where
147+ end (Sink sink) time = sink.end time
148+
149+createEndCombinator :: forall a. (Sink a -> Time -> Unit) -> Stream a -> Stream a
150+createEndCombinator end stream = createCombinator event end stream where
151+ event (Sink sink) time value = sink.event time value
152+
153+tapEvent :: forall e a. (a -> Eff e Unit) -> Stream a -> Stream a
154+tapEvent f stream = createEventCombinator event stream where
155+ event :: Sink a -> Time -> a -> Unit
156+ event (Sink sink) time value = sink.event time (always value (unsafePerformEff (f value)))
157+
158+tapEnd :: forall e a. Eff e Unit -> Stream a -> Stream a
159+tapEnd f stream = createEndCombinator end stream where
160+ end :: Sink a -> Time -> Unit
161+ end (Sink sink) time = sink.end (always time (unsafePerformEff f))
162+
163+take :: forall a. Int -> Stream a -> Stream a
164+take 0 stream = empty
165+take amount (Stream stream) = Stream { run } where
166+ run (Sink sink) scheduler = disposable where
167+ disposable = stream.run (Sink { event, end }) scheduler where
168+ state :: State Int
169+ state = createState amount
170+
171+ event ::Time -> a -> Unit
172+ event time value = do
173+ let amountLeft = (getState state)
174+ if amountLeft == 0
175+ then unit
176+ else do
177+ let newAmountLeft = state.set \x -> x - 1
178+ let x = sink.event time value
179+ if newAmountLeft == 0
180+ then sink.end (always time (runPure disposable.dispose))
181+ else unit
182+
183+ end time = sink.end time
184+
185+skip :: forall a. Int -> Stream a -> Stream a
186+skip 0 stream = stream
187+skip amount stream = createEventCombinator event stream where
188+ state :: State Int
189+ state = createState amount
190+
191+ event :: Sink a -> Time -> a -> Unit
192+ event (Sink sink) time value = do
193+ let amountLeft = runPure state.get
194+ if (amountLeft == 0)
195+ then sink.event time value
196+ else always unit (state.set \x -> x - 1)
197+
198+mapStream :: forall a b. (a -> b) -> Stream a -> Stream b
199+mapStream f stream = createEventCombinator event stream where
200+ event :: Sink b -> Time -> a -> Unit
201+ event (Sink sink) time value = sink.event time (f value)
202+
203+filter :: forall a. (a -> Boolean) -> Stream a -> Stream a
204+filter predicate stream = createEventCombinator event stream where
205+ event (Sink sink) time value = if predicate value
206+ then sink.event time value
207+ else unit
208+
209+skipRepeatsWith :: forall a. (a -> a -> Boolean) -> Stream a -> Stream a
210+skipRepeatsWith compare stream = createEventCombinator event stream where
211+ state :: State (Maybe a)
212+ state = createState Nothing
213+
214+ event (Sink sink) time value = do
215+ let currentState = (getState state)
216+ if (isNothing currentState)
217+ then sink.event time value
218+ else do
219+ let currentValue = unsafePartial (fromJust currentState)
220+ if (compare currentValue value)
221+ then unit
222+ else sink.event time value
223+
224+skipRepeats :: forall a. (Eq a) => Stream a -> Stream a
225+skipRepeats = skipRepeatsWith (eq)
226+
227+constant :: forall a b. b -> Stream a -> Stream b
228+constant b stream = createEventCombinator event stream where
229+ event :: Sink b -> Time -> a -> Unit
230+ event (Sink sink) time value = sink.event time b
231+
232+concat :: forall a. Stream a -> Stream a -> Stream a
233+concat s1 s2 = continueWith(\_ -> s2) s1
234+
235+startWith :: forall a. a -> Stream a -> Stream a
236+startWith seed stream = concat (just seed) stream
237+
238+scan :: forall a b. (b -> a -> b) -> b -> Stream a -> Stream b
239+scan f initial stream = startWith initial (createEventCombinator event stream) where
240+ seed :: State b
241+ seed = createState initial
242+
243+ event :: Sink b -> Time -> a -> Unit
244+ event (Sink sink) time value = sink.event time (seed.set \x -> f x value)
245+
246+loop :: forall a b c. (b -> a -> { seed :: b, value :: c }) -> b -> Stream a -> Stream c
247+loop f initial stream = createEventCombinator event stream where
248+ seed :: State b
249+ seed = createState initial
250+
251+ event :: Sink c -> Time -> a -> Unit
252+ event (Sink sink) time x = do
253+ let seedValue = f (getState seed) x
254+ sink.event time (always seedValue.value (seed.set \_ -> seedValue.seed))
255+
256+concatMap :: forall a b. (a -> Stream b) -> Stream a -> Stream b
257+concatMap f stream = mergeMapConcurrently f 1 stream
258+
259+ap :: forall a b. (Stream (a -> b)) -> Stream a -> Stream b
260+ap fs xs = combine2 (\f x -> f x) fs xs
261+
262+during :: forall a b. Stream (Stream b) -> Stream a -> Stream a
263+during timeWindow stream = until (join timeWindow) (since timeWindow stream)
264+
265+sample :: forall a b c. (a -> b -> c) -> Stream a -> Stream b -> Stream c
266+sample f (Stream sampler) (Stream stream) = Stream { run: runSample (createState Nothing) } where
267+ runSample :: State (Maybe b) -> Sink c -> Scheduler -> Disposable
268+ runSample state sink scheduler = disposeAll
269+ [ stream.run (createHoldSink state) scheduler
270+ , sampler.run (createSampleSink state sink f) scheduler
271+ ]
272+
273+createHoldSink :: forall a. State (Maybe a) -> Sink a
274+createHoldSink state = Sink { event, end } where
275+ event time value = always unit (state.set \_ -> Just value)
276+ end time = unit
277+
278+createSampleSink :: forall a b c. State (Maybe b) -> Sink c -> (a -> b -> c) -> Sink a
279+createSampleSink state (Sink sink) f = Sink { event, end } where
280+ end time = sink.end time
281+ event time value =
282+ if isNothing (runPure state.get)
283+ then unit
284+ else sink.event time (f value (unsafePartial (fromJust (runPure state.get))))
285+
286+sampleWith :: forall a b. Stream b -> Stream a -> Stream a
287+sampleWith signal stream = sample (\_ y -> y) signal stream
288+
289+-- find a better way to perform these side effects
290+always :: forall a b. a -> b -> a
291+always a b = a
292+
293+type State a = { get:: Pure a, set :: (a -> a) -> a }
294+
295+getState :: forall a. State a -> a
296+getState state = runPure state.get
297+
298+createState :: forall a. a -> State a
299+createState seed = { set, get }
300+ where
301+ ref :: Ref a
302+ ref = unsafePerformEff (unsafeCoerceEff (newRef seed))
303+
304+ set :: (a -> a) -> a
305+ set f = unsafePerformEff (unsafeCoerceEff (modifyRef' ref \x -> { state: (f x), value: (f x) }))
306+
307+ get :: Pure a
308+ get = unsafeCoerceEff (readRef ref)
309+
310+foreign import drain :: forall e a. Stream a -> EffStream e Unit
311+foreign import subscribe :: forall e a. (a -> Eff e Unit) -> (Unit -> Eff e Unit) -> Stream a -> EffStream e Unit
312+foreign import fromArray :: forall a b. Array a -> Stream b -> Stream a
313+foreign import chain :: forall a b. Stream a -> (a -> Stream b) -> Stream b
314+foreign import mergeConcurrently :: forall a. Int -> Stream (Stream a) -> Stream a
315+foreign import join :: forall a. Stream (Stream a) -> Stream a
316+foreign import mergeMapConcurrently :: forall a b. (a -> Stream b) -> Int -> Stream a -> Stream b
317+foreign import switch :: forall a. Stream (Stream a) -> Stream a
318+foreign import continueWith :: forall a. (Unit -> Stream a) -> Stream a -> Stream a
319+foreign import combine2 :: forall a b c. (a -> b -> c) -> Stream a -> Stream b -> Stream c
320+foreign import combine3 :: forall a b c d. (a -> b -> c -> d) -> Stream a -> Stream b -> Stream c -> Stream d
321+foreign import combine4 :: forall a b c d e. (a -> b -> c -> d -> e) -> Stream a -> Stream b -> Stream c -> Stream d -> Stream e
322+foreign import combine5 :: forall a b c d e f. (a -> b -> c -> d -> e -> f) -> Stream a -> Stream b -> Stream c -> Stream d -> Stream e -> Stream f
323+foreign import combineArray :: forall a. Array (Stream a) -> Stream (Array a)
324+foreign import merge :: forall a. Array (Stream a) -> Stream a
325+foreign import until :: forall a b. Stream b -> Stream a -> Stream a
326+foreign import since :: forall a b. Stream b -> Stream a -> Stream a
327+foreign import debounce :: forall a. Number -> Stream a -> Stream a
328+foreign import throttle :: forall a. Number -> Stream a -> Stream a
329+foreign import delay :: forall a. Number -> Stream a -> Stream a
330+foreign import multicast :: forall a. Stream a -> Stream a
331+foreign import hold :: forall a. Stream a -> Stream a
332+foreign import proxy :: forall a. a -> { attach :: (Stream a -> Stream a), stream :: Stream a }
333+
334+-- Sad subject types :/
335+foreign import toSubject :: forall a. Stream a -> Subject a
336+foreign import toHoldSubject :: forall a. Int -> Stream a -> Subject a
337+foreign import fromSubject :: forall a. Subject a -> Stream a
338+foreign import subjectEvent :: forall a. a -> Subject a -> Subject a
339+foreign import subjectEnd :: forall a. a -> Subject a -> Subject a
src/Control/Time.pursnew file+3
@@ -0,0 +1,3 @@
1+module Control.Stream.Time where
2+
3+newtype Time = Time Int