mirror of
https://github.com/ilyakooo0/purescript-stream.git
synced 2024-09-11 06:15:36 +03:00
feat(stream): implement more helpers and combinators
This commit is contained in:
parent
820bb35cd5
commit
8e8b5166ce
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@
|
|||||||
/generated-docs/
|
/generated-docs/
|
||||||
/.psc*
|
/.psc*
|
||||||
/.psa*
|
/.psa*
|
||||||
|
src/Main.purs
|
||||||
|
@ -9,8 +9,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"purescript-eff": "^2.0.0",
|
"purescript-eff": "^2.0.0",
|
||||||
"purescript-prelude": "^2.5.0",
|
"purescript-prelude": "^2.5.0",
|
||||||
"purescript-aff-promise": "^0.4.0",
|
"purescript-refs": "^2.0.0"
|
||||||
"purescript-aff": "^2.0.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"purescript-console": "^2.0.0",
|
"purescript-console": "^2.0.0",
|
||||||
|
97
ffi/Control/Stream/combinators/combine.js
Normal file
97
ffi/Control/Stream/combinators/combine.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/** @license MIT License (c) copyright 2010-2016 original author or authors */
|
||||||
|
/** @author Brian Cavalier */
|
||||||
|
/** @author John Hann */
|
||||||
|
|
||||||
|
import { map, tail } from '@most/prelude'
|
||||||
|
|
||||||
|
import { IndexSink } from '../sink/IndexSink'
|
||||||
|
import { Stream } from '../Stream'
|
||||||
|
import { disposeAll } from '../disposables'
|
||||||
|
|
||||||
|
export const combine2 = f => s1 => s2 => combineArray(f, [s1, s2])
|
||||||
|
export const combine3 = f => s1 => s2 => s3 => combineArray(f, [s1, s2, s3])
|
||||||
|
export const combine4 = f => s1 => s2 => s3 => s4 => combineArray(f, [s1, s2, s3, s4])
|
||||||
|
export const combine5 = f => s1 => s2 => s3 => s4 => s5 => combineArray(f, [s1, s2, s3, s4, s5])
|
||||||
|
|
||||||
|
function combineArray (f, streams) {
|
||||||
|
return new Stream(combineSources(f, streams))
|
||||||
|
}
|
||||||
|
|
||||||
|
function combineSources (f, streams) {
|
||||||
|
return new Combine(f, map(getSource, streams))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSource (stream) {
|
||||||
|
return stream.source
|
||||||
|
}
|
||||||
|
|
||||||
|
function Combine (f, sources) {
|
||||||
|
this.f = f
|
||||||
|
this.sources = sources
|
||||||
|
}
|
||||||
|
|
||||||
|
Combine.prototype.run = function (sink) {
|
||||||
|
return scheduler => {
|
||||||
|
var l = this.sources.length
|
||||||
|
var disposables = new Array(l)
|
||||||
|
var sinks = new Array(l)
|
||||||
|
|
||||||
|
var mergeSink = new CombineSink(disposables, sinks, sink, this.f)
|
||||||
|
|
||||||
|
for (var indexSink, i = 0; i < l; ++i) {
|
||||||
|
indexSink = sinks[i] = new IndexSink(i, mergeSink)
|
||||||
|
disposables[i] = this.sources[i].run(indexSink)(scheduler)
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposeAll(disposables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function CombineSink (disposables, sinks, sink, f) {
|
||||||
|
this.sink = sink
|
||||||
|
this.disposables = disposables
|
||||||
|
this.sinks = sinks
|
||||||
|
this.f = f
|
||||||
|
|
||||||
|
var l = sinks.length
|
||||||
|
this.awaiting = l
|
||||||
|
this.values = new Array(l)
|
||||||
|
this.hasValue = new Array(l)
|
||||||
|
|
||||||
|
for (var i = 0; i < l; ++i) {
|
||||||
|
this.hasValue[i] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeCount = sinks.length
|
||||||
|
}
|
||||||
|
|
||||||
|
CombineSink.prototype.event = function (t) {
|
||||||
|
return indexedValue => {
|
||||||
|
var i = indexedValue.index
|
||||||
|
var awaiting = this._updateReady(i)
|
||||||
|
|
||||||
|
this.values[i] = indexedValue.value
|
||||||
|
if (awaiting === 0) {
|
||||||
|
const value = tail(this.values).reduce((f, x) => f(x), this.f(this.values[0]))
|
||||||
|
this.sink.event(t)(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CombineSink.prototype._updateReady = function (index) {
|
||||||
|
if (this.awaiting > 0) {
|
||||||
|
if (!this.hasValue[index]) {
|
||||||
|
this.hasValue[index] = true
|
||||||
|
this.awaiting -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.awaiting
|
||||||
|
}
|
||||||
|
|
||||||
|
CombineSink.prototype.end = function (t, indexedValue) {
|
||||||
|
this.disposables[indexedValue.index].dispose()
|
||||||
|
|
||||||
|
if (--this.activeCount === 0) {
|
||||||
|
this.sink.end(t)
|
||||||
|
}
|
||||||
|
}
|
56
ffi/Control/Stream/combinators/concat.js
Normal file
56
ffi/Control/Stream/combinators/concat.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { MemoizedDisposable } from '../disposables/MemoizedDisposable'
|
||||||
|
import { Stream } from '../Stream'
|
||||||
|
|
||||||
|
export const concat = s1 => s2 => continueWith(() => s2)(s1)
|
||||||
|
|
||||||
|
export const continueWith = f => stream => new Stream(new ContinueWith(f, stream.source))
|
||||||
|
|
||||||
|
function ContinueWith (f, source) {
|
||||||
|
this.f = f
|
||||||
|
this.source = source
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueWith.prototype.run = function (sink) {
|
||||||
|
return scheduler => {
|
||||||
|
return new ContinueWithSink(this.f, this.source, sink, scheduler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ContinueWithSink (f, source, sink, scheduler) {
|
||||||
|
this.f = f
|
||||||
|
this.sink = sink
|
||||||
|
this.scheduler = scheduler
|
||||||
|
this.active = true
|
||||||
|
this.disposable = new MemoizedDisposable(source.run(this)(scheduler))
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueWithSink.prototype.event = function (t) {
|
||||||
|
return x => {
|
||||||
|
if (!this.active) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.sink.event(t)(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueWithSink.prototype.end = function (t, x) {
|
||||||
|
if (!this.active) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.disposable.dispose()
|
||||||
|
this._startNext(t, x, this.sink)
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueWithSink.prototype._startNext = function (t, x, sink) {
|
||||||
|
this._continue(this.f, x, sink)
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueWithSink.prototype._continue = function (f, x, sink) {
|
||||||
|
return f(x).source.run(sink)(this.scheduler)
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueWithSink.prototype.dispose = function () {
|
||||||
|
this.active = false
|
||||||
|
return this.disposable.dispose()
|
||||||
|
}
|
@ -1,2 +1,6 @@
|
|||||||
export * from './tap'
|
|
||||||
export * from './drain'
|
export * from './drain'
|
||||||
|
export * from './mergeConcurrently'
|
||||||
|
export * from './combine'
|
||||||
|
export * from './merge'
|
||||||
|
export * from './concat'
|
||||||
|
export * from './switch'
|
||||||
|
74
ffi/Control/Stream/combinators/merge.js
Normal file
74
ffi/Control/Stream/combinators/merge.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/** @license MIT License (c) copyright 2010-2016 original author or authors */
|
||||||
|
/** @author Brian Cavalier */
|
||||||
|
/** @author John Hann */
|
||||||
|
|
||||||
|
import { IndexSink } from '../sink/IndexSink'
|
||||||
|
import { Stream } from '../Stream'
|
||||||
|
import { disposeAll } from '../disposables'
|
||||||
|
import { reduce } from '@most/prelude'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} streams array of stream to merge
|
||||||
|
* @returns {Stream} stream containing events from all input observables
|
||||||
|
* in time order. If two events are simultaneous they will be merged in
|
||||||
|
* arbitrary order.
|
||||||
|
*/
|
||||||
|
export function merge (streams) {
|
||||||
|
return new Stream(mergeSources(streams))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implements fusion/flattening for merge. It will
|
||||||
|
* fuse adjacent merge operations. For example:
|
||||||
|
* - a.merge(b).merge(c) effectively becomes merge(a, b, c)
|
||||||
|
* - merge(a, merge(b, c)) effectively becomes merge(a, b, c)
|
||||||
|
* It does this by concatenating the sources arrays of
|
||||||
|
* any nested Merge sources, in effect "flattening" nested
|
||||||
|
* merge operations into a single merge.
|
||||||
|
*/
|
||||||
|
function mergeSources (streams) {
|
||||||
|
return new Merge(reduce(appendSources, [], streams))
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendSources (sources, stream) {
|
||||||
|
var source = stream.source
|
||||||
|
return source instanceof Merge
|
||||||
|
? sources.concat(source.sources)
|
||||||
|
: sources.concat(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Merge (sources) {
|
||||||
|
this.sources = sources
|
||||||
|
}
|
||||||
|
|
||||||
|
Merge.prototype.run = function (sink, scheduler) {
|
||||||
|
var l = this.sources.length
|
||||||
|
var disposables = new Array(l)
|
||||||
|
var sinks = new Array(l)
|
||||||
|
|
||||||
|
var mergeSink = new MergeSink(disposables, sinks, sink)
|
||||||
|
|
||||||
|
for (var indexSink, i = 0; i < l; ++i) {
|
||||||
|
indexSink = sinks[i] = new IndexSink(i, mergeSink)
|
||||||
|
disposables[i] = this.sources[i].run(indexSink, scheduler)
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposeAll(disposables)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MergeSink (disposables, sinks, sink) {
|
||||||
|
this.sink = sink
|
||||||
|
this.disposables = disposables
|
||||||
|
this.activeCount = sinks.length
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeSink.prototype.event = function (t, indexValue) {
|
||||||
|
this.sink.event(t, indexValue.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeSink.prototype.end = function (t, indexedValue) {
|
||||||
|
dispose.tryDispose(t, this.disposables[indexedValue.index], this.sink)
|
||||||
|
if (--this.activeCount === 0) {
|
||||||
|
this.sink.end(t, indexedValue.value)
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,23 @@
|
|||||||
/** @author Brian Cavalier */
|
/** @author Brian Cavalier */
|
||||||
/** @author John Hann */
|
/** @author John Hann */
|
||||||
|
|
||||||
|
import { curry2, curry3, id as identity } from '@most/prelude'
|
||||||
|
|
||||||
import { LinkedList } from '../LinkedList'
|
import { LinkedList } from '../LinkedList'
|
||||||
import { MemoizedDisosable } from '../disposables/MemoizedDisposable'
|
import { MemoizedDisposable } from '../disposables/MemoizedDisposable'
|
||||||
import { id as identity } from '@most/prelude'
|
import { Stream } from '../Stream'
|
||||||
|
|
||||||
export const mergeConcurrently = (concurrency, stream) =>
|
export const flatMap =
|
||||||
mergeMapConcurrently(identity, concurrency, stream)
|
curry2((stream, f) => mergeMapConcurrently(f, Infinity, stream))
|
||||||
|
|
||||||
export const mergeMapConcurrently = (f, concurrency, stream) =>
|
export const mergeConcurrently = curry2((concurrency, stream) =>
|
||||||
new MergeConcurrently(f, concurrency, stream)
|
mergeMapConcurrently(identity, concurrency, stream))
|
||||||
|
|
||||||
|
export const join =
|
||||||
|
mergeConcurrently(Infinity)
|
||||||
|
|
||||||
|
export const mergeMapConcurrently = curry3((f, concurrency, stream) =>
|
||||||
|
new Stream(new MergeConcurrently(f, concurrency, stream.source)))
|
||||||
|
|
||||||
class MergeConcurrently {
|
class MergeConcurrently {
|
||||||
constructor (f, concurrency, source) {
|
constructor (f, concurrency, source) {
|
||||||
@ -19,8 +27,8 @@ class MergeConcurrently {
|
|||||||
this.source = source
|
this.source = source
|
||||||
}
|
}
|
||||||
|
|
||||||
run (sink, scheduler) {
|
run (sink) {
|
||||||
return new Outer(this.f, this.concurrency, this.source, sink, scheduler)
|
return scheduler => new Outer(this.f, this.concurrency, this.source, sink, scheduler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,12 +40,12 @@ class Outer {
|
|||||||
this.scheduler = scheduler
|
this.scheduler = scheduler
|
||||||
this.pending = []
|
this.pending = []
|
||||||
this.current = new LinkedList()
|
this.current = new LinkedList()
|
||||||
this.disposable = new MemoizedDisosable(source.run(this, scheduler))
|
this.disposable = new MemoizedDisposable(source.run(this)(scheduler))
|
||||||
this.active = true
|
this.active = true
|
||||||
}
|
}
|
||||||
|
|
||||||
event (t, x) {
|
event (t) {
|
||||||
this._addInner(t, x)
|
return x => this._addInner(t, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
_addInner (t, x) {
|
_addInner (t, x) {
|
||||||
@ -62,10 +70,10 @@ class Outer {
|
|||||||
this.current.add(innerSink)
|
this.current.add(innerSink)
|
||||||
}
|
}
|
||||||
|
|
||||||
end (t, x) {
|
end (t) {
|
||||||
this.active = false
|
this.active = false
|
||||||
this.disposable.dispose()
|
this.disposable.dispose()
|
||||||
this._checkEnd(t, x)
|
this._checkEnd(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose () {
|
dispose () {
|
||||||
@ -75,26 +83,25 @@ class Outer {
|
|||||||
this.current.dispose()
|
this.current.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
_endInner (t, x, inner) {
|
_endInner (t, inner) {
|
||||||
this.current.remove(inner)
|
this.current.remove(inner)
|
||||||
inner.dispose()
|
inner.dispose()
|
||||||
|
|
||||||
if (this.pending.length === 0) {
|
if (this.pending.length === 0) {
|
||||||
this._checkEnd(t, x)
|
this._checkEnd(t)
|
||||||
} else {
|
} else {
|
||||||
this._startInner(t, this.pending.shift())
|
this._startInner(t, this.pending.shift())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkEnd (t, x) {
|
_checkEnd (t) {
|
||||||
if (!this.active && this.current.isEmpty()) {
|
if (!this.active && this.current.isEmpty()) {
|
||||||
this.sink.end(t, x)
|
this.sink.end(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapAndRun = (f, x, sink, scheduler) =>
|
const mapAndRun = (f, x, sink, scheduler) => f(x).source.run(sink)(scheduler)
|
||||||
f(x).run(sink, scheduler)
|
|
||||||
|
|
||||||
class Inner {
|
class Inner {
|
||||||
constructor (time, outer, sink) {
|
constructor (time, outer, sink) {
|
||||||
@ -105,16 +112,12 @@ class Inner {
|
|||||||
this.disposable = void 0
|
this.disposable = void 0
|
||||||
}
|
}
|
||||||
|
|
||||||
event (t, x) {
|
event (t) {
|
||||||
this.sink.event(Math.max(t, this.time), x)
|
return x => this.sink.event(Math.max(t, this.time))(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
end (t, x) {
|
end (t) {
|
||||||
this.outer._endInner(Math.max(t, this.time), x, this)
|
this.outer._endInner(Math.max(t, this.time), this)
|
||||||
}
|
|
||||||
|
|
||||||
error (t, e) {
|
|
||||||
this.outer.error(Math.max(t, this.time), e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose () {
|
dispose () {
|
||||||
|
99
ffi/Control/Stream/combinators/switch.js
Normal file
99
ffi/Control/Stream/combinators/switch.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { disposeAll, emptyDisposable } from '../disposables'
|
||||||
|
|
||||||
|
import { Stream } from '../Stream'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a stream of streams, return a new stream that adopts the behavior
|
||||||
|
* of the most recent inner stream.
|
||||||
|
* @param {Stream} stream of streams on which to switch
|
||||||
|
* @returns {Stream} switching stream
|
||||||
|
*/
|
||||||
|
function switchLatest (stream) {
|
||||||
|
return new Stream(new Switch(stream.source))
|
||||||
|
}
|
||||||
|
|
||||||
|
export { switchLatest as switch }
|
||||||
|
|
||||||
|
function Switch (source) {
|
||||||
|
this.source = source
|
||||||
|
}
|
||||||
|
|
||||||
|
Switch.prototype.run = function (sink) {
|
||||||
|
return scheduler => {
|
||||||
|
const switchSink = new SwitchSink(sink, scheduler)
|
||||||
|
return disposeAll([switchSink, this.source.run(switchSink)(scheduler)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SwitchSink (sink, scheduler) {
|
||||||
|
this.sink = sink
|
||||||
|
this.scheduler = scheduler
|
||||||
|
this.current = null
|
||||||
|
this.ended = false
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchSink.prototype.event = function (t) {
|
||||||
|
return stream => {
|
||||||
|
this._disposeCurrent(t) // TODO: capture the result of this dispose
|
||||||
|
this.current = new Segment(t, Infinity, this, this.sink)
|
||||||
|
this.current.disposable = stream.source.run(this.current)(this.scheduler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchSink.prototype.end = function (t, x) {
|
||||||
|
this.ended = true
|
||||||
|
this._checkEnd(t, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchSink.prototype.dispose = function () {
|
||||||
|
return this._disposeCurrent(this.scheduler.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchSink.prototype._disposeCurrent = function (t) {
|
||||||
|
if (this.current !== null) {
|
||||||
|
return this.current._dispose(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchSink.prototype._disposeInner = function (t, inner) {
|
||||||
|
inner._dispose(t) // TODO: capture the result of this dispose
|
||||||
|
if (inner === this.current) {
|
||||||
|
this.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchSink.prototype._checkEnd = function (t, x) {
|
||||||
|
if (this.ended && this.current === null) {
|
||||||
|
this.sink.end(t, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchSink.prototype._endInner = function (t, x, inner) {
|
||||||
|
this._disposeInner(t, inner)
|
||||||
|
this._checkEnd(t, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Segment (min, max, outer, sink) {
|
||||||
|
this.min = min
|
||||||
|
this.max = max
|
||||||
|
this.outer = outer
|
||||||
|
this.sink = sink
|
||||||
|
this.disposable = emptyDisposable
|
||||||
|
}
|
||||||
|
|
||||||
|
Segment.prototype.event = function (t) {
|
||||||
|
return x => {
|
||||||
|
if (t < this.max) {
|
||||||
|
this.sink.event(Math.max(t, this.min))(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Segment.prototype.end = function (t, x) {
|
||||||
|
this.outer._endInner(Math.max(t, this.min), x, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
Segment.prototype._dispose = function (t) {
|
||||||
|
this.max = t
|
||||||
|
this.disposable.dispose()
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
import { Stream } from '../Stream'
|
|
||||||
import { curry2 } from '@most/prelude'
|
|
||||||
|
|
||||||
export const tapEnd = curry2((f, stream) => new Stream(new TapEnd(f, stream.source)))
|
|
||||||
|
|
||||||
export const tapEvent = curry2((f, stream) => new Stream(new TapEvent(f, stream.source)))
|
|
||||||
|
|
||||||
class TapEvent {
|
|
||||||
constructor (f, source) {
|
|
||||||
this.source = source
|
|
||||||
this.f = f
|
|
||||||
|
|
||||||
this.run = sink => scheduler =>
|
|
||||||
source.run(new TapSink(f, sink))(scheduler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TapSink {
|
|
||||||
constructor (f, sink) {
|
|
||||||
this.event = t => x => {
|
|
||||||
f(x)()
|
|
||||||
sink.event(t)(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.end = t => {
|
|
||||||
sink.end(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TapEnd {
|
|
||||||
constructor (f, source) {
|
|
||||||
this.source = source
|
|
||||||
this.f = f
|
|
||||||
|
|
||||||
this.run = sink => scheduler =>
|
|
||||||
source.run(new TapEndSink(f, sink))(scheduler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TapEndSink {
|
|
||||||
constructor (f, sink) {
|
|
||||||
this.event = t => x => sink.event(t)(x)
|
|
||||||
|
|
||||||
this.end = t => {
|
|
||||||
f()
|
|
||||||
sink.end(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,9 +7,7 @@ export class Disposable {
|
|||||||
constructor (dispose, data) {
|
constructor (dispose, data) {
|
||||||
this._dispose = dispose
|
this._dispose = dispose
|
||||||
this._data = data
|
this._data = data
|
||||||
}
|
|
||||||
|
|
||||||
dispose () {
|
this.dispose = () => dispose(data)
|
||||||
return this._dispose(this._data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ export class MemoizedDisposable {
|
|||||||
dispose () {
|
dispose () {
|
||||||
if (!this.disposed) {
|
if (!this.disposed) {
|
||||||
this.disposed = true
|
this.disposed = true
|
||||||
this.value = disposeSafely(this.disposable)
|
this.value = this.disposable.dispose()
|
||||||
this.disposable = undefined
|
this.disposable = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,4 +9,4 @@ export const createDisposable = curry2((dispose, data) => new MemoizedDisposable
|
|||||||
export const emptyDisposable = new Disposable(id, undefined)
|
export const emptyDisposable = new Disposable(id, undefined)
|
||||||
|
|
||||||
export const disposeAll = disposables =>
|
export const disposeAll = disposables =>
|
||||||
createDisposable(map(disposeSafely, disposables), disposables)
|
createDisposable(curry2(map)(disposeSafely), disposables)
|
||||||
|
@ -11,6 +11,8 @@ function runSource (source, scheduler) {
|
|||||||
const observer = new RunEffectsSink(disposable)
|
const observer = new RunEffectsSink(disposable)
|
||||||
|
|
||||||
disposable.setDisposable(source.run(observer)(scheduler))
|
disposable.setDisposable(source.run(observer)(scheduler))
|
||||||
|
|
||||||
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
var RunEffectsSink = function RunEffectsSink (disposable) {
|
var RunEffectsSink = function RunEffectsSink (disposable) {
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
export function ScheduledTask (delay, period, task, scheduler) {
|
export class ScheduledTask {
|
||||||
this.time = delay
|
constructor (delay, period, task, scheduler) {
|
||||||
this.period = period
|
this.time = delay
|
||||||
this.task = task
|
this.period = period
|
||||||
this.scheduler = scheduler
|
this.task = task
|
||||||
this.active = true
|
this.scheduler = scheduler
|
||||||
|
this.active = true
|
||||||
|
}
|
||||||
|
|
||||||
this.run = () => task.run(this.time)
|
run () {
|
||||||
this.dispose = () => {
|
this.task.run(this.time)
|
||||||
scheduler.cancel(this)
|
}
|
||||||
task.dispose()
|
|
||||||
|
dispose () {
|
||||||
|
this.scheduler.cancel(this)
|
||||||
|
this.task.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,24 +25,26 @@ export class Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asap (task) {
|
asap (task) {
|
||||||
return this.schedule(0, -1, task)
|
return this.schedule(0)(-1)(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
delay (delay, task) {
|
delay (delay) {
|
||||||
return this.schedule(delay, -1, task)
|
return task => this.schedule(delay)(-1)(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
periodic (period, task) {
|
periodic (period) {
|
||||||
return this.schedule(0, period, task)
|
return task => this.schedule(0)(period)(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule (delay, period, task) {
|
schedule (delay) {
|
||||||
const now = this.now()
|
return period => task => {
|
||||||
const st = new ScheduledTask(now + Math.max(0, delay), period, task, this)
|
const now = this.now()
|
||||||
|
const st = new ScheduledTask(now + Math.max(0, delay), period, task, this)
|
||||||
|
|
||||||
this.timeline.add(st)
|
this.timeline.add(st)
|
||||||
this._scheduleNextRun(now)
|
this._scheduleNextRun(now)
|
||||||
return st
|
return st
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel (task) {
|
cancel (task) {
|
||||||
|
@ -15,10 +15,10 @@ export class IndexSink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end (t, x) {
|
end (t) {
|
||||||
if (!this.active) return
|
if (!this.active) return
|
||||||
|
|
||||||
this.active = false
|
this.active = false
|
||||||
this.sink.end(t, { index: this.index, value: x })
|
this.sink.end(t, { index: this.index })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"start": "yarn build && pulp run",
|
||||||
"build": "rollup -c && pulp build",
|
"build": "rollup -c && pulp build",
|
||||||
"changelog": "conventional-changelog -i CHANGELOG.md -s -r 0 -p angular",
|
"changelog": "conventional-changelog -i CHANGELOG.md -s -r 0 -p angular",
|
||||||
"clean": "rimraf lib lib.es2015",
|
"clean": "rimraf lib lib.es2015",
|
||||||
|
@ -16,11 +16,38 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|||||||
|
|
||||||
// drop :: Int -> [a] -> [a]
|
// drop :: Int -> [a] -> [a]
|
||||||
// drop first n elements
|
// drop first n elements
|
||||||
|
function drop (n, a) { // eslint-disable-line complexity
|
||||||
|
if (n < 0) {
|
||||||
|
throw new TypeError('n must be >= 0')
|
||||||
|
}
|
||||||
|
|
||||||
|
var l = a.length;
|
||||||
|
if (n === 0 || l === 0) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n >= l) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return unsafeDrop(n, a, l - n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafeDrop :: Int -> [a] -> Int -> [a]
|
||||||
|
// Internal helper for drop
|
||||||
|
function unsafeDrop (n, a, l) {
|
||||||
|
var b = new Array(l);
|
||||||
|
for (var i = 0; i < l; ++i) {
|
||||||
|
b[i] = a[n + i];
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// tail :: [a] -> [a]
|
// tail :: [a] -> [a]
|
||||||
// drop head element
|
// drop head element
|
||||||
|
function tail (a) {
|
||||||
|
return drop(1, a)
|
||||||
|
}
|
||||||
|
|
||||||
// copy :: [a] -> [a]
|
// copy :: [a] -> [a]
|
||||||
// duplicate a (shallow duplication)
|
// duplicate a (shallow duplication)
|
||||||
@ -39,7 +66,13 @@ function map (f, a) {
|
|||||||
|
|
||||||
// reduce :: (a -> b -> a) -> a -> [b] -> a
|
// reduce :: (a -> b -> a) -> a -> [b] -> a
|
||||||
// accumulate via left-fold
|
// accumulate via left-fold
|
||||||
|
function reduce (f, z, a) {
|
||||||
|
var r = z;
|
||||||
|
for (var i = 0, l = a.length; i < l; ++i) {
|
||||||
|
r = f(r, a[i], i);
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
// replace :: a -> Int -> [a]
|
// replace :: a -> Int -> [a]
|
||||||
// replace element at index
|
// replace element at index
|
||||||
@ -124,10 +157,8 @@ function curry3 (f) {
|
|||||||
var Disposable = function Disposable (dispose, data) {
|
var Disposable = function Disposable (dispose, data) {
|
||||||
this._dispose = dispose;
|
this._dispose = dispose;
|
||||||
this._data = data;
|
this._data = data;
|
||||||
};
|
|
||||||
|
|
||||||
Disposable.prototype.dispose = function dispose () {
|
this.dispose = function () { return dispose(data); };
|
||||||
return this._dispose(this._data)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var MemoizedDisposable = function MemoizedDisposable (disposable) {
|
var MemoizedDisposable = function MemoizedDisposable (disposable) {
|
||||||
@ -139,14 +170,14 @@ var MemoizedDisposable = function MemoizedDisposable (disposable) {
|
|||||||
MemoizedDisposable.prototype.dispose = function dispose () {
|
MemoizedDisposable.prototype.dispose = function dispose () {
|
||||||
if (!this.disposed) {
|
if (!this.disposed) {
|
||||||
this.disposed = true;
|
this.disposed = true;
|
||||||
this.value = disposeSafely(this.disposable);
|
this.value = this.disposable.dispose();
|
||||||
this.disposable = undefined;
|
this.disposable = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.value
|
return this.value
|
||||||
};
|
};
|
||||||
|
|
||||||
function disposeSafely$1 (disposable) {
|
function disposeSafely (disposable) {
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +185,7 @@ var createDisposable = curry2(function (dispose, data) { return new MemoizedDisp
|
|||||||
|
|
||||||
var emptyDisposable = new Disposable(id, undefined);
|
var emptyDisposable = new Disposable(id, undefined);
|
||||||
|
|
||||||
var disposeAll = function (disposables) { return createDisposable(map(disposeSafely$1, disposables), disposables); };
|
var disposeAll = function (disposables) { return createDisposable(curry2(map)(disposeSafely), disposables); };
|
||||||
|
|
||||||
/** @license MIT License (c) copyright 2010-2016 original author or authors */
|
/** @license MIT License (c) copyright 2010-2016 original author or authors */
|
||||||
/** @author Brian Cavalier */
|
/** @author Brian Cavalier */
|
||||||
@ -202,21 +233,22 @@ function runAsap (f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
function ScheduledTask (delay, period, task, scheduler) {
|
var ScheduledTask = function ScheduledTask (delay, period, task, scheduler) {
|
||||||
var this$1 = this;
|
|
||||||
|
|
||||||
this.time = delay;
|
this.time = delay;
|
||||||
this.period = period;
|
this.period = period;
|
||||||
this.task = task;
|
this.task = task;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.active = true;
|
this.active = true;
|
||||||
|
};
|
||||||
|
|
||||||
this.run = function () { return task.run(this$1.time); };
|
ScheduledTask.prototype.run = function run () {
|
||||||
this.dispose = function () {
|
this.task.run(this.time);
|
||||||
scheduler.cancel(this$1);
|
};
|
||||||
task.dispose();
|
|
||||||
};
|
ScheduledTask.prototype.dispose = function dispose () {
|
||||||
}
|
this.scheduler.cancel(this);
|
||||||
|
this.task.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
function runTask$1 (task) {
|
function runTask$1 (task) {
|
||||||
@ -244,24 +276,32 @@ Scheduler.prototype.now = function now () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Scheduler.prototype.asap = function asap (task) {
|
Scheduler.prototype.asap = function asap (task) {
|
||||||
return this.schedule(0, -1, task)
|
return this.schedule(0)(-1)(task)
|
||||||
};
|
};
|
||||||
|
|
||||||
Scheduler.prototype.delay = function delay (delay$1, task) {
|
Scheduler.prototype.delay = function delay (delay$1) {
|
||||||
return this.schedule(delay$1, -1, task)
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (task) { return this$1.schedule(delay$1)(-1)(task); }
|
||||||
};
|
};
|
||||||
|
|
||||||
Scheduler.prototype.periodic = function periodic (period, task) {
|
Scheduler.prototype.periodic = function periodic (period) {
|
||||||
return this.schedule(0, period, task)
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (task) { return this$1.schedule(0)(period)(task); }
|
||||||
};
|
};
|
||||||
|
|
||||||
Scheduler.prototype.schedule = function schedule (delay, period, task) {
|
Scheduler.prototype.schedule = function schedule (delay) {
|
||||||
var now = this.now();
|
var this$1 = this;
|
||||||
var st = new ScheduledTask(now + Math.max(0, delay), period, task, this);
|
|
||||||
|
|
||||||
this.timeline.add(st);
|
return function (period) { return function (task) {
|
||||||
this._scheduleNextRun(now);
|
var now = this$1.now();
|
||||||
return st
|
var st = new ScheduledTask(now + Math.max(0, delay), period, task, this$1);
|
||||||
|
|
||||||
|
this$1.timeline.add(st);
|
||||||
|
this$1._scheduleNextRun(now);
|
||||||
|
return st
|
||||||
|
}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
Scheduler.prototype.cancel = function cancel (task) {
|
Scheduler.prototype.cancel = function cancel (task) {
|
||||||
@ -478,48 +518,6 @@ var scheduleTasks = curry2(function (tasks, scheduler) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var Stream = function Stream (source) {
|
|
||||||
this.source = source;
|
|
||||||
};
|
|
||||||
|
|
||||||
var tapEnd = curry2(function (f, stream) { return new Stream(new TapEnd(f, stream.source)); });
|
|
||||||
|
|
||||||
var tapEvent = curry2(function (f, stream) { return new Stream(new TapEvent(f, stream.source)); });
|
|
||||||
|
|
||||||
var TapEvent = function TapEvent (f, source) {
|
|
||||||
this.source = source;
|
|
||||||
this.f = f;
|
|
||||||
|
|
||||||
this.run = function (sink) { return function (scheduler) { return source.run(new TapSink(f, sink))(scheduler); }; };
|
|
||||||
};
|
|
||||||
|
|
||||||
var TapSink = function TapSink (f, sink) {
|
|
||||||
this.event = function (t) { return function (x) {
|
|
||||||
f(x)();
|
|
||||||
sink.event(t)(x);
|
|
||||||
}; };
|
|
||||||
|
|
||||||
this.end = function (t) {
|
|
||||||
sink.end(t);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var TapEnd = function TapEnd (f, source) {
|
|
||||||
this.source = source;
|
|
||||||
this.f = f;
|
|
||||||
|
|
||||||
this.run = function (sink) { return function (scheduler) { return source.run(new TapEndSink(f, sink))(scheduler); }; };
|
|
||||||
};
|
|
||||||
|
|
||||||
var TapEndSink = function TapEndSink (f, sink) {
|
|
||||||
this.event = function (t) { return function (x) { return sink.event(t)(x); }; };
|
|
||||||
|
|
||||||
this.end = function (t) {
|
|
||||||
f();
|
|
||||||
sink.end(t);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var SettableDisposable = function SettableDisposable () {
|
var SettableDisposable = function SettableDisposable () {
|
||||||
this.disposable = void 0;
|
this.disposable = void 0;
|
||||||
this.disposed = false;
|
this.disposed = false;
|
||||||
@ -559,6 +557,8 @@ function runSource (source, scheduler) {
|
|||||||
var observer = new RunEffectsSink(disposable);
|
var observer = new RunEffectsSink(disposable);
|
||||||
|
|
||||||
disposable.setDisposable(source.run(observer)(scheduler));
|
disposable.setDisposable(source.run(observer)(scheduler));
|
||||||
|
|
||||||
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
var RunEffectsSink = function RunEffectsSink (disposable) {
|
var RunEffectsSink = function RunEffectsSink (disposable) {
|
||||||
@ -577,100 +577,526 @@ RunEffectsSink.prototype.end = function end (t) {
|
|||||||
|
|
||||||
var drain = function (stream) { return function () { return runEffects(stream, defaultScheduler); }; };
|
var drain = function (stream) { return function () { return runEffects(stream, defaultScheduler); }; };
|
||||||
|
|
||||||
var combine =
|
var LinkedList = function LinkedList () {
|
||||||
curry3(function (f, stream1, stream2) { return new Stream(new Combine(f, stream1.source, stream2.source)); });
|
this.head = null;
|
||||||
|
this.length = 0;
|
||||||
var Combine = function Combine (f, source1, source2) {
|
|
||||||
this.f = f;
|
|
||||||
this.source1 = source1;
|
|
||||||
this.source2 = source2;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Combine.prototype.run = function run (sink) {
|
LinkedList.prototype.add = function add (x) {
|
||||||
var this$1 = this;
|
if (this.head !== null) {
|
||||||
|
this.head.prev = x;
|
||||||
|
x.next = this.head;
|
||||||
|
}
|
||||||
|
this.head = x;
|
||||||
|
++this.length;
|
||||||
|
};
|
||||||
|
|
||||||
return function (scheduler) {
|
LinkedList.prototype.remove = function remove (x) { // eslint-disable-linecomplexity
|
||||||
var disposables = Array(2);
|
--this.length;
|
||||||
var sinks = Array(2);
|
if (x === this.head) {
|
||||||
|
this.head = this.head.next;
|
||||||
var mergeSink = new CombineSink(disposables, sinks, sink, this$1.f);
|
}
|
||||||
|
if (x.next !== null) {
|
||||||
sinks[0] = new IndexSink(0, mergeSink);
|
x.next.prev = x.prev;
|
||||||
sinks[1] = new IndexSink(1, mergeSink);
|
x.next = null;
|
||||||
disposables[0] = this$1.source1.run(sinks[0])(scheduler);
|
}
|
||||||
disposables[1] = this$1.source2.run(sinks[1])(scheduler);
|
if (x.prev !== null) {
|
||||||
|
x.prev.next = x.next;
|
||||||
return disposeAll(disposables)
|
x.prev = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var IndexSink = function IndexSink () {};
|
LinkedList.prototype.isEmpty = function isEmpty () {
|
||||||
|
return this.length === 0
|
||||||
|
};
|
||||||
|
|
||||||
IndexSink.prototype.cosntructor = function cosntructor (i, sink) {
|
LinkedList.prototype.dispose = function dispose () {
|
||||||
this.i = i;
|
if (this.isEmpty()) { return }
|
||||||
|
|
||||||
|
var x = this.head;
|
||||||
|
this.head = null;
|
||||||
|
this.length = 0;
|
||||||
|
|
||||||
|
while (x !== null) {
|
||||||
|
x.dispose();
|
||||||
|
x = x.next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var Stream = function Stream (source) {
|
||||||
|
this.source = source;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @license MIT License (c) copyright 2010-2016 original author or authors */
|
||||||
|
/** @author Brian Cavalier */
|
||||||
|
/** @author John Hann */
|
||||||
|
|
||||||
|
var flatMap =
|
||||||
|
curry2(function (stream, f) { return mergeMapConcurrently(f, Infinity, stream); });
|
||||||
|
|
||||||
|
var mergeConcurrently = curry2(function (concurrency, stream) { return mergeMapConcurrently(id, concurrency, stream); });
|
||||||
|
|
||||||
|
var join =
|
||||||
|
mergeConcurrently(Infinity);
|
||||||
|
|
||||||
|
var mergeMapConcurrently = curry3(function (f, concurrency, stream) { return new Stream(new MergeConcurrently(f, concurrency, stream.source)); });
|
||||||
|
|
||||||
|
var MergeConcurrently = function MergeConcurrently (f, concurrency, source) {
|
||||||
|
this.f = f;
|
||||||
|
this.concurrency = concurrency;
|
||||||
|
this.source = source;
|
||||||
|
};
|
||||||
|
|
||||||
|
MergeConcurrently.prototype.run = function run (sink) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (scheduler) { return new Outer(this$1.f, this$1.concurrency, this$1.source, sink, scheduler); }
|
||||||
|
};
|
||||||
|
|
||||||
|
var Outer = function Outer (f, concurrency, source, sink, scheduler) {
|
||||||
|
this.f = f;
|
||||||
|
this.concurrency = concurrency;
|
||||||
this.sink = sink;
|
this.sink = sink;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
this.pending = [];
|
||||||
|
this.current = new LinkedList();
|
||||||
|
this.disposable = new MemoizedDisposable(source.run(this)(scheduler));
|
||||||
|
this.active = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
Outer.prototype.event = function event (t) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (x) { return this$1._addInner(t, x); }
|
||||||
|
};
|
||||||
|
|
||||||
|
Outer.prototype._addInner = function _addInner (t, x) {
|
||||||
|
if (this.current.length < this.concurrency) {
|
||||||
|
this._startInner(t, x);
|
||||||
|
} else {
|
||||||
|
this.pending.push(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Outer.prototype._startInner = function _startInner (t, x) {
|
||||||
|
try {
|
||||||
|
this._initInner(t, x);
|
||||||
|
} catch (e) {
|
||||||
|
this.error(t, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Outer.prototype._initInner = function _initInner (t, x) {
|
||||||
|
var innerSink = new Inner(t, this, this.sink);
|
||||||
|
innerSink.disposable = mapAndRun(this.f, x, innerSink, this.scheduler);
|
||||||
|
this.current.add(innerSink);
|
||||||
|
};
|
||||||
|
|
||||||
|
Outer.prototype.end = function end (t) {
|
||||||
|
this.active = false;
|
||||||
|
this.disposable.dispose();
|
||||||
|
this._checkEnd(t);
|
||||||
|
};
|
||||||
|
|
||||||
|
Outer.prototype.dispose = function dispose () {
|
||||||
|
this.active = false;
|
||||||
|
this.pending.length = 0;
|
||||||
|
this.disposable.dispose();
|
||||||
|
this.current.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
Outer.prototype._endInner = function _endInner (t, inner) {
|
||||||
|
this.current.remove(inner);
|
||||||
|
inner.dispose();
|
||||||
|
|
||||||
|
if (this.pending.length === 0) {
|
||||||
|
this._checkEnd(t);
|
||||||
|
} else {
|
||||||
|
this._startInner(t, this.pending.shift());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Outer.prototype._checkEnd = function _checkEnd (t) {
|
||||||
|
if (!this.active && this.current.isEmpty()) {
|
||||||
|
this.sink.end(t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var mapAndRun = function (f, x, sink, scheduler) { return f(x).source.run(sink)(scheduler); };
|
||||||
|
|
||||||
|
var Inner = function Inner (time, outer, sink) {
|
||||||
|
this.prev = this.next = null;
|
||||||
|
this.time = time;
|
||||||
|
this.outer = outer;
|
||||||
|
this.sink = sink;
|
||||||
|
this.disposable = void 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Inner.prototype.event = function event (t) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (x) { return this$1.sink.event(Math.max(t, this$1.time))(x); }
|
||||||
|
};
|
||||||
|
|
||||||
|
Inner.prototype.end = function end (t) {
|
||||||
|
this.outer._endInner(Math.max(t, this.time), this);
|
||||||
|
};
|
||||||
|
|
||||||
|
Inner.prototype.dispose = function dispose () {
|
||||||
|
return this.disposable.dispose()
|
||||||
|
};
|
||||||
|
|
||||||
|
var IndexSink = function IndexSink (i, sink) {
|
||||||
|
this.sink = sink;
|
||||||
|
this.index = i;
|
||||||
|
this.active = true;
|
||||||
|
this.value = void 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
IndexSink.prototype.event = function event (t) {
|
IndexSink.prototype.event = function event (t) {
|
||||||
var this$1 = this;
|
var this$1 = this;
|
||||||
|
|
||||||
return function (x) {
|
return function (x) {
|
||||||
this$1.sink.event(t)({ index: this$1.i, value: x });
|
if (!this$1.active) { return }
|
||||||
|
|
||||||
|
this$1.value = x;
|
||||||
|
this$1.sink.event(t)(this$1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
IndexSink.prototype.end = function end (t) {
|
IndexSink.prototype.end = function end (t) {
|
||||||
this.sink.end(t, this.i);
|
if (!this.active) { return }
|
||||||
|
|
||||||
|
this.active = false;
|
||||||
|
this.sink.end(t, { index: this.index });
|
||||||
};
|
};
|
||||||
|
|
||||||
var CombineSink = function CombineSink (disposables, sinks, sink, f) {
|
/** @license MIT License (c) copyright 2010-2016 original author or authors */
|
||||||
|
/** @author Brian Cavalier */
|
||||||
|
/** @author John Hann */
|
||||||
|
|
||||||
|
var combine2 = function (f) { return function (s1) { return function (s2) { return combineArray(f, [s1, s2]); }; }; };
|
||||||
|
var combine3 = function (f) { return function (s1) { return function (s2) { return function (s3) { return combineArray(f, [s1, s2, s3]); }; }; }; };
|
||||||
|
var combine4 = function (f) { return function (s1) { return function (s2) { return function (s3) { return function (s4) { return combineArray(f, [s1, s2, s3, s4]); }; }; }; }; };
|
||||||
|
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]); }; }; }; }; }; };
|
||||||
|
|
||||||
|
function combineArray (f, streams) {
|
||||||
|
return new Stream(combineSources(f, streams))
|
||||||
|
}
|
||||||
|
|
||||||
|
function combineSources (f, streams) {
|
||||||
|
return new Combine(f, map(getSource, streams))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSource (stream) {
|
||||||
|
return stream.source
|
||||||
|
}
|
||||||
|
|
||||||
|
function Combine (f, sources) {
|
||||||
|
this.f = f;
|
||||||
|
this.sources = sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
Combine.prototype.run = function (sink) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (scheduler) {
|
||||||
|
var l = this$1.sources.length;
|
||||||
|
var disposables = new Array(l);
|
||||||
|
var sinks = new Array(l);
|
||||||
|
|
||||||
|
var mergeSink = new CombineSink(disposables, sinks, sink, this$1.f);
|
||||||
|
|
||||||
|
for (var indexSink, i = 0; i < l; ++i) {
|
||||||
|
indexSink = sinks[i] = new IndexSink(i, mergeSink);
|
||||||
|
disposables[i] = this$1.sources[i].run(indexSink)(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposeAll(disposables)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function CombineSink (disposables, sinks, sink, f) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
this.sink = sink;
|
||||||
this.disposables = disposables;
|
this.disposables = disposables;
|
||||||
this.sinks = sinks;
|
this.sinks = sinks;
|
||||||
this.sink = sink;
|
|
||||||
this.f = f;
|
this.f = f;
|
||||||
|
|
||||||
this.awaiting = 2;
|
var l = sinks.length;
|
||||||
this.values = Array(2);
|
this.awaiting = l;
|
||||||
this.hasValue = [false, false];
|
this.values = new Array(l);
|
||||||
this.activeCount = 2;
|
this.hasValue = new Array(l);
|
||||||
};
|
|
||||||
|
|
||||||
CombineSink.prototype.event = function event (t) {
|
for (var i = 0; i < l; ++i) {
|
||||||
var this$1 = this;
|
this$1.hasValue[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
return function (ref) {
|
this.activeCount = sinks.length;
|
||||||
var index = ref.index;
|
}
|
||||||
var value = ref.value;
|
|
||||||
|
|
||||||
var f = this$1.f;
|
CombineSink.prototype.event = function (t) {
|
||||||
var awaiting = this$1._updateReady(index);
|
var this$1 = this;
|
||||||
this$1.values[index] = value;
|
|
||||||
|
|
||||||
|
return function (indexedValue) {
|
||||||
|
var i = indexedValue.index;
|
||||||
|
var awaiting = this$1._updateReady(i);
|
||||||
|
|
||||||
|
this$1.values[i] = indexedValue.value;
|
||||||
if (awaiting === 0) {
|
if (awaiting === 0) {
|
||||||
var result = f(this$1.values[0])(this$1.values[1]);
|
var value = tail(this$1.values).reduce(function (f, x) { return f(x); }, this$1.f(this$1.values[0]));
|
||||||
this$1.sink.event(t)(result);
|
this$1.sink.event(t)(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CombineSink.prototype._updateReady = function _updateReady (index) {
|
CombineSink.prototype._updateReady = function (index) {
|
||||||
if (this.awaiting > 0) {
|
if (this.awaiting > 0) {
|
||||||
if (!this.hasValue[index]) {
|
if (!this.hasValue[index]) {
|
||||||
this.hasValue[index] = true;
|
this.hasValue[index] = true;
|
||||||
this.awaiting -= 1;
|
this.awaiting -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.awaiting
|
return this.awaiting
|
||||||
};
|
};
|
||||||
|
|
||||||
CombineSink.prototype.end = function end (t, ref) {
|
CombineSink.prototype.end = function (t, indexedValue) {
|
||||||
var index = ref.index;
|
this.disposables[indexedValue.index].dispose();
|
||||||
var value = ref.value;
|
|
||||||
|
|
||||||
this.disposables[index].dispose();
|
if (--this.activeCount === 0) {
|
||||||
|
this.sink.end(t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (--this.activeCount === 0) { this.sink.end(t); }
|
/** @license MIT License (c) copyright 2010-2016 original author or authors */
|
||||||
|
/** @author Brian Cavalier */
|
||||||
|
/** @author John Hann */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} streams array of stream to merge
|
||||||
|
* @returns {Stream} stream containing events from all input observables
|
||||||
|
* in time order. If two events are simultaneous they will be merged in
|
||||||
|
* arbitrary order.
|
||||||
|
*/
|
||||||
|
function merge (streams) {
|
||||||
|
return new Stream(mergeSources(streams))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implements fusion/flattening for merge. It will
|
||||||
|
* fuse adjacent merge operations. For example:
|
||||||
|
* - a.merge(b).merge(c) effectively becomes merge(a, b, c)
|
||||||
|
* - merge(a, merge(b, c)) effectively becomes merge(a, b, c)
|
||||||
|
* It does this by concatenating the sources arrays of
|
||||||
|
* any nested Merge sources, in effect "flattening" nested
|
||||||
|
* merge operations into a single merge.
|
||||||
|
*/
|
||||||
|
function mergeSources (streams) {
|
||||||
|
return new Merge(reduce(appendSources, [], streams))
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendSources (sources, stream) {
|
||||||
|
var source = stream.source;
|
||||||
|
return source instanceof Merge
|
||||||
|
? sources.concat(source.sources)
|
||||||
|
: sources.concat(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Merge (sources) {
|
||||||
|
this.sources = sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
Merge.prototype.run = function (sink, scheduler) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
var l = this.sources.length;
|
||||||
|
var disposables = new Array(l);
|
||||||
|
var sinks = new Array(l);
|
||||||
|
|
||||||
|
var mergeSink = new MergeSink(disposables, sinks, sink);
|
||||||
|
|
||||||
|
for (var indexSink, i = 0; i < l; ++i) {
|
||||||
|
indexSink = sinks[i] = new IndexSink(i, mergeSink);
|
||||||
|
disposables[i] = this$1.sources[i].run(indexSink, scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposeAll(disposables)
|
||||||
|
};
|
||||||
|
|
||||||
|
function MergeSink (disposables, sinks, sink) {
|
||||||
|
this.sink = sink;
|
||||||
|
this.disposables = disposables;
|
||||||
|
this.activeCount = sinks.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeSink.prototype.event = function (t, indexValue) {
|
||||||
|
this.sink.event(t, indexValue.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
MergeSink.prototype.end = function (t, indexedValue) {
|
||||||
|
dispose.tryDispose(t, this.disposables[indexedValue.index], this.sink);
|
||||||
|
if (--this.activeCount === 0) {
|
||||||
|
this.sink.end(t, indexedValue.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var concat = function (s1) { return function (s2) { return continueWith(function () { return s2; })(s1); }; };
|
||||||
|
|
||||||
|
var continueWith = function (f) { return function (stream) { return new Stream(new ContinueWith(f, stream.source)); }; };
|
||||||
|
|
||||||
|
function ContinueWith (f, source) {
|
||||||
|
this.f = f;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueWith.prototype.run = function (sink) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (scheduler) {
|
||||||
|
return new ContinueWithSink(this$1.f, this$1.source, sink, scheduler)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function ContinueWithSink (f, source, sink, scheduler) {
|
||||||
|
this.f = f;
|
||||||
|
this.sink = sink;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
this.active = true;
|
||||||
|
this.disposable = new MemoizedDisposable(source.run(this)(scheduler));
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueWithSink.prototype.event = function (t) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (x) {
|
||||||
|
if (!this$1.active) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this$1.sink.event(t)(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ContinueWithSink.prototype.end = function (t, x) {
|
||||||
|
if (!this.active) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.disposable.dispose();
|
||||||
|
this._startNext(t, x, this.sink);
|
||||||
|
};
|
||||||
|
|
||||||
|
ContinueWithSink.prototype._startNext = function (t, x, sink) {
|
||||||
|
this._continue(this.f, x, sink);
|
||||||
|
};
|
||||||
|
|
||||||
|
ContinueWithSink.prototype._continue = function (f, x, sink) {
|
||||||
|
return f(x).source.run(sink)(this.scheduler)
|
||||||
|
};
|
||||||
|
|
||||||
|
ContinueWithSink.prototype.dispose = function () {
|
||||||
|
this.active = false;
|
||||||
|
return this.disposable.dispose()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a stream of streams, return a new stream that adopts the behavior
|
||||||
|
* of the most recent inner stream.
|
||||||
|
* @param {Stream} stream of streams on which to switch
|
||||||
|
* @returns {Stream} switching stream
|
||||||
|
*/
|
||||||
|
function switchLatest (stream) {
|
||||||
|
return new Stream(new Switch(stream.source))
|
||||||
|
}
|
||||||
|
|
||||||
|
function Switch (source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
Switch.prototype.run = function (sink) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (scheduler) {
|
||||||
|
var switchSink = new SwitchSink(sink, scheduler);
|
||||||
|
return disposeAll([switchSink, this$1.source.run(switchSink)(scheduler)])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function SwitchSink (sink, scheduler) {
|
||||||
|
this.sink = sink;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
this.current = null;
|
||||||
|
this.ended = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchSink.prototype.event = function (t) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (stream) {
|
||||||
|
this$1._disposeCurrent(t); // TODO: capture the result of this dispose
|
||||||
|
this$1.current = new Segment(t, Infinity, this$1, this$1.sink);
|
||||||
|
this$1.current.disposable = stream.source.run(this$1.current)(this$1.scheduler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SwitchSink.prototype.end = function (t, x) {
|
||||||
|
this.ended = true;
|
||||||
|
this._checkEnd(t, x);
|
||||||
|
};
|
||||||
|
|
||||||
|
SwitchSink.prototype.dispose = function () {
|
||||||
|
return this._disposeCurrent(this.scheduler.now())
|
||||||
|
};
|
||||||
|
|
||||||
|
SwitchSink.prototype._disposeCurrent = function (t) {
|
||||||
|
if (this.current !== null) {
|
||||||
|
return this.current._dispose(t)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SwitchSink.prototype._disposeInner = function (t, inner) {
|
||||||
|
inner._dispose(t); // TODO: capture the result of this dispose
|
||||||
|
if (inner === this.current) {
|
||||||
|
this.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SwitchSink.prototype._checkEnd = function (t, x) {
|
||||||
|
if (this.ended && this.current === null) {
|
||||||
|
this.sink.end(t, x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SwitchSink.prototype._endInner = function (t, x, inner) {
|
||||||
|
this._disposeInner(t, inner);
|
||||||
|
this._checkEnd(t, x);
|
||||||
|
};
|
||||||
|
|
||||||
|
function Segment (min, max, outer, sink) {
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
this.outer = outer;
|
||||||
|
this.sink = sink;
|
||||||
|
this.disposable = emptyDisposable;
|
||||||
|
}
|
||||||
|
|
||||||
|
Segment.prototype.event = function (t) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
return function (x) {
|
||||||
|
if (t < this$1.max) {
|
||||||
|
this$1.sink.event(Math.max(t, this$1.min))(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Segment.prototype.end = function (t, x) {
|
||||||
|
this.outer._endInner(Math.max(t, this.min), x, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
Segment.prototype._dispose = function (t) {
|
||||||
|
this.max = t;
|
||||||
|
this.disposable.dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
@ -682,7 +1108,16 @@ exports.defaultScheduler = defaultScheduler;
|
|||||||
exports.scheduleTasks = scheduleTasks;
|
exports.scheduleTasks = scheduleTasks;
|
||||||
exports.endTask = endTask;
|
exports.endTask = endTask;
|
||||||
exports.eventTask = eventTask;
|
exports.eventTask = eventTask;
|
||||||
exports.tapEnd = tapEnd;
|
|
||||||
exports.tapEvent = tapEvent;
|
|
||||||
exports.drain = drain;
|
exports.drain = drain;
|
||||||
exports.combine = combine;
|
exports.flatMap = flatMap;
|
||||||
|
exports.mergeConcurrently = mergeConcurrently;
|
||||||
|
exports.join = join;
|
||||||
|
exports.mergeMapConcurrently = mergeMapConcurrently;
|
||||||
|
exports.combine2 = combine2;
|
||||||
|
exports.combine3 = combine3;
|
||||||
|
exports.combine4 = combine4;
|
||||||
|
exports.combine5 = combine5;
|
||||||
|
exports.merge = merge;
|
||||||
|
exports.concat = concat;
|
||||||
|
exports.continueWith = continueWith;
|
||||||
|
exports.switch = switchLatest;
|
||||||
|
@ -10,9 +10,9 @@ module Control.Stream
|
|||||||
, ScheduledTask
|
, ScheduledTask
|
||||||
, Disposable
|
, Disposable
|
||||||
, Time
|
, Time
|
||||||
, createStream
|
, EventFn
|
||||||
, getSource
|
, EndFn
|
||||||
, runSource
|
-- Scheduler-related functions and values
|
||||||
, defaultScheduler
|
, defaultScheduler
|
||||||
, eventTask
|
, eventTask
|
||||||
, endTask
|
, endTask
|
||||||
@ -20,19 +20,54 @@ module Control.Stream
|
|||||||
, emptyDisposable
|
, emptyDisposable
|
||||||
, disposeAll
|
, disposeAll
|
||||||
, scheduleTasks
|
, scheduleTasks
|
||||||
|
-- Stream-related helpers
|
||||||
|
, createStream
|
||||||
|
, createSink
|
||||||
|
, createCombinator
|
||||||
|
, createEventCombinator
|
||||||
|
, getSource
|
||||||
|
, runSource
|
||||||
|
-- Combinators
|
||||||
, drain
|
, drain
|
||||||
, tapEvent
|
, tapEvent
|
||||||
, tapEnd
|
, tapEnd
|
||||||
|
, filter
|
||||||
|
, mergeConcurrently
|
||||||
|
, mergeMapConcurrently
|
||||||
|
, join
|
||||||
|
, combine2
|
||||||
|
, combine3
|
||||||
|
, combine4
|
||||||
|
, combine5
|
||||||
|
, continueWith
|
||||||
|
, concat
|
||||||
|
, merge
|
||||||
|
, switch
|
||||||
|
, constant
|
||||||
|
, scan
|
||||||
|
-- Stream factories
|
||||||
, just
|
, just
|
||||||
, fromArray
|
, fromArray
|
||||||
|
, empty
|
||||||
|
, periodic
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Control.Applicative (class Applicative)
|
||||||
|
import Control.Apply (class Apply)
|
||||||
|
import Control.Bind (class Bind)
|
||||||
|
import Control.Category (id)
|
||||||
|
import Control.Monad (class Monad)
|
||||||
import Control.Monad.Eff (Eff, Pure)
|
import Control.Monad.Eff (Eff, Pure)
|
||||||
|
import Control.Monad.Eff.Ref (Ref, modifyRef', newRef, readRef)
|
||||||
|
import Control.Monad.Eff.Unsafe (unsafeCoerceEff, unsafePerformEff)
|
||||||
|
import Control.MonadPlus (class Alt, class Plus)
|
||||||
import Data.Array (snoc)
|
import Data.Array (snoc)
|
||||||
|
import Data.Function (flip, ($))
|
||||||
import Data.Functor (class Functor, map)
|
import Data.Functor (class Functor, map)
|
||||||
import Data.Unit (Unit)
|
import Data.Monoid (class Monoid)
|
||||||
import Prelude (flip)
|
import Data.Semigroup (class Semigroup)
|
||||||
|
import Data.Unit (Unit, unit)
|
||||||
|
|
||||||
newtype Time = Time Int
|
newtype Time = Time Int
|
||||||
|
|
||||||
@ -50,11 +85,11 @@ type Sink a =
|
|||||||
, end :: Time -> Unit
|
, end :: Time -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
type Scheduler =
|
newtype Scheduler = Scheduler
|
||||||
{ now :: Pure Time
|
{ now :: Pure Time
|
||||||
, asap :: Task -> ScheduledTask
|
, asap :: Task -> ScheduledTask
|
||||||
, delay :: Number -> Task -> ScheduledTask
|
, delay :: Number -> Task -> ScheduledTask
|
||||||
, period :: Number -> Task -> ScheduledTask
|
, periodic :: Number -> Task -> ScheduledTask
|
||||||
, schedule :: Number -> Number -> Task -> ScheduledTask
|
, schedule :: Number -> Number -> Task -> ScheduledTask
|
||||||
, cancel :: ScheduledTask -> Unit
|
, cancel :: ScheduledTask -> Unit
|
||||||
, cancelAll :: (ScheduledTask -> Boolean) -> Unit
|
, cancelAll :: (ScheduledTask -> Boolean) -> Unit
|
||||||
@ -72,9 +107,36 @@ type ScheduledTask =
|
|||||||
|
|
||||||
type Disposable = { dispose :: Pure Unit }
|
type Disposable = { dispose :: Pure Unit }
|
||||||
|
|
||||||
|
type EventFn a b = (Sink b -> Scheduler -> Time -> a -> Unit)
|
||||||
|
type EndFn a = (Sink a -> Scheduler -> Time -> Unit)
|
||||||
|
|
||||||
instance functorStream :: Functor Stream where
|
instance functorStream :: Functor Stream where
|
||||||
map = _map
|
map = _map
|
||||||
|
|
||||||
|
instance applyStream :: Apply Stream where
|
||||||
|
apply = combine2 id
|
||||||
|
|
||||||
|
instance applicativeStream :: Applicative Stream where
|
||||||
|
pure = just
|
||||||
|
|
||||||
|
instance bindStream :: Bind Stream where
|
||||||
|
bind = flatMap
|
||||||
|
|
||||||
|
instance monadStream :: Monad Stream
|
||||||
|
|
||||||
|
instance semigroupStream :: Semigroup (Stream a) where
|
||||||
|
append = concat
|
||||||
|
|
||||||
|
instance altStream :: Alt Stream where
|
||||||
|
alt = \s1 s2 -> merge [s1, s2]
|
||||||
|
|
||||||
|
instance monoidStream :: Monoid (Stream a) where
|
||||||
|
mempty = empty
|
||||||
|
|
||||||
|
instance plusStream :: Plus Stream where
|
||||||
|
empty = empty
|
||||||
|
|
||||||
|
-- Stream-related helpers
|
||||||
createStream :: forall a. (Sink a -> Scheduler -> Disposable) -> Stream a
|
createStream :: forall a. (Sink a -> Scheduler -> Disposable) -> Stream a
|
||||||
createStream run = Stream { source: { run } }
|
createStream run = Stream { source: { run } }
|
||||||
|
|
||||||
@ -84,6 +146,30 @@ getSource stream = case stream of Stream a -> a.source
|
|||||||
runSource :: forall a. Stream a -> Sink a -> Scheduler -> Disposable
|
runSource :: forall a. Stream a -> Sink a -> Scheduler -> Disposable
|
||||||
runSource stream sink scheduler = (getSource stream).run sink scheduler
|
runSource stream sink scheduler = (getSource stream).run sink scheduler
|
||||||
|
|
||||||
|
createSink :: forall a. (Time -> a -> Unit) -> (Time -> Unit) -> Sink a
|
||||||
|
createSink event end = { event, end }
|
||||||
|
|
||||||
|
createCombinator :: forall a b. EventFn a b -> EndFn b -> Stream a -> Stream b
|
||||||
|
createCombinator event end stream = createStream runCombinator
|
||||||
|
where
|
||||||
|
runCombinator :: Sink b -> Scheduler -> Disposable
|
||||||
|
runCombinator sink scheduler =
|
||||||
|
runSource stream (createSink (event sink scheduler) (end sink scheduler)) scheduler
|
||||||
|
|
||||||
|
createEventCombinator :: forall a b. EventFn a b -> Stream a -> Stream b
|
||||||
|
createEventCombinator event stream = createCombinator event end stream
|
||||||
|
where
|
||||||
|
end :: EndFn b
|
||||||
|
end sink scheduler time =
|
||||||
|
sink.end time
|
||||||
|
|
||||||
|
createEndCombinator :: forall a. EndFn a -> Stream a -> Stream a
|
||||||
|
createEndCombinator end stream = createCombinator event end stream
|
||||||
|
where
|
||||||
|
event :: EventFn a a
|
||||||
|
event sink scheduler time value = sink.event time value
|
||||||
|
|
||||||
|
-- Stream factories
|
||||||
just :: forall a. a -> Stream a
|
just :: forall a. a -> Stream a
|
||||||
just a = createStream (runJust a)
|
just a = createStream (runJust a)
|
||||||
|
|
||||||
@ -91,6 +177,12 @@ runJust :: forall a. a -> Sink a -> Scheduler -> Disposable
|
|||||||
runJust a sink scheduler =
|
runJust a sink scheduler =
|
||||||
scheduleTasks [ eventTask a sink, endTask sink ] scheduler
|
scheduleTasks [ eventTask a sink, endTask sink ] scheduler
|
||||||
|
|
||||||
|
empty :: forall a. Stream a
|
||||||
|
empty = createStream \sink scheduler -> scheduleTasks [endTask sink] scheduler
|
||||||
|
|
||||||
|
never :: forall a. Stream a
|
||||||
|
never = createStream \sink scheduler -> emptyDisposable
|
||||||
|
|
||||||
-- TODO: remove fromArray because it's not a real event stream
|
-- TODO: remove fromArray because it's not a real event stream
|
||||||
-- replace with a fromArray combinator
|
-- replace with a fromArray combinator
|
||||||
fromArray :: forall a. Array a -> Stream a
|
fromArray :: forall a. Array a -> Stream a
|
||||||
@ -105,19 +197,78 @@ runFromArray arr sink scheduler = scheduleTasks tasks scheduler
|
|||||||
eventTasks :: Array Task
|
eventTasks :: Array Task
|
||||||
eventTasks = map (flip eventTask sink) arr
|
eventTasks = map (flip eventTask sink) arr
|
||||||
|
|
||||||
|
periodic :: Number -> Stream Unit
|
||||||
|
periodic period = createStream (runPeriodic period)
|
||||||
|
|
||||||
|
runPeriodic :: Number -> Sink Unit -> Scheduler -> Disposable
|
||||||
|
runPeriodic period sink scheduler = { dispose: scheduledTask.dispose }
|
||||||
|
where
|
||||||
|
p = case scheduler of Scheduler a -> a.periodic
|
||||||
|
scheduledTask = p period (eventTask unit sink)
|
||||||
|
|
||||||
|
-- combinators
|
||||||
_map :: forall a b. (a -> b) -> Stream a -> Stream b
|
_map :: forall a b. (a -> b) -> Stream a -> Stream b
|
||||||
_map f stream = createStream runMap
|
_map f stream = createEventCombinator mapEvent stream
|
||||||
where
|
where
|
||||||
runMap :: Sink b -> Scheduler -> Disposable
|
mapEvent :: EventFn a b
|
||||||
runMap sink scheduler =
|
mapEvent sink scheduler time value = sink.event time (f value)
|
||||||
runSource stream (mapSink f sink) scheduler
|
|
||||||
|
|
||||||
mapSink :: forall a b. (a -> b) -> Sink b -> Sink a
|
filter :: forall a. (a -> Boolean) -> Stream a -> Stream a
|
||||||
mapSink f sink = { event: mapEvent, end: sink.end }
|
filter predicate stream = createEventCombinator event stream
|
||||||
where
|
where
|
||||||
mapEvent :: Time -> a -> Unit
|
event :: EventFn a a
|
||||||
mapEvent time value = sink.event time (f value)
|
event sink scheduler time value = if predicate value
|
||||||
|
then sink.event time value
|
||||||
|
else unit
|
||||||
|
|
||||||
|
tapEvent :: forall e a. (a -> EffStream e Unit) -> Stream a -> Stream a
|
||||||
|
tapEvent f stream = createEventCombinator event stream
|
||||||
|
where
|
||||||
|
event :: EventFn a a
|
||||||
|
event sink scheduler time value = sink.event time value
|
||||||
|
where -- find a better way to perform these side effects
|
||||||
|
x = unsafePerformEff (f value)
|
||||||
|
|
||||||
|
tapEnd :: forall e a. EffStream e Unit -> Stream a -> Stream a
|
||||||
|
tapEnd f stream = createEndCombinator end stream
|
||||||
|
where
|
||||||
|
end :: EndFn a
|
||||||
|
end sink scheduler time = sink.end time
|
||||||
|
where -- find a better way to perform these side effects
|
||||||
|
x = unsafePerformEff f
|
||||||
|
|
||||||
|
startWith :: forall a. a -> Stream a -> Stream a
|
||||||
|
startWith value stream = concat (just value) stream
|
||||||
|
|
||||||
|
constant :: forall a b. b -> Stream a -> Stream b
|
||||||
|
constant value stream = createEventCombinator constantEvent stream
|
||||||
|
where
|
||||||
|
constantEvent sink scheduler time x = sink.event time value
|
||||||
|
|
||||||
|
scan :: forall a b. (b -> a -> b) -> b -> Stream a -> Stream b
|
||||||
|
scan f seed stream = startWith seed $ createEventCombinator scanEvent stream
|
||||||
|
where
|
||||||
|
state = createState seed
|
||||||
|
|
||||||
|
scanEvent :: Sink b -> Scheduler -> Time -> a -> Unit
|
||||||
|
scanEvent sink scheduler time value = sink.event time (state.set \acc -> f acc value)
|
||||||
|
|
||||||
|
-- find a better way to perform these side effects
|
||||||
|
createState :: forall a. a -> { get :: Pure a, set :: (a -> a) -> a }
|
||||||
|
createState seed = { set, get }
|
||||||
|
where
|
||||||
|
ref :: Ref a
|
||||||
|
ref = unsafePerformEff $ unsafeCoerceEff $ newRef seed
|
||||||
|
|
||||||
|
set :: (a -> a) -> a
|
||||||
|
set f = unsafePerformEff $ unsafeCoerceEff $
|
||||||
|
modifyRef' ref \x -> { state: (f x), value: (f x) }
|
||||||
|
|
||||||
|
get :: Pure a
|
||||||
|
get = unsafeCoerceEff $ readRef ref
|
||||||
|
|
||||||
|
-- Foreign imports
|
||||||
|
-- Scheduler-related
|
||||||
foreign import defaultScheduler :: Scheduler
|
foreign import defaultScheduler :: Scheduler
|
||||||
foreign import eventTask :: forall a. a -> Sink a -> Task
|
foreign import eventTask :: forall a. a -> Sink a -> Task
|
||||||
foreign import endTask :: forall a. Sink a -> Task
|
foreign import endTask :: forall a. Sink a -> Task
|
||||||
@ -125,6 +276,17 @@ foreign import createDisposable :: forall a. (a -> Unit) -> a -> Disposable
|
|||||||
foreign import emptyDisposable :: Disposable
|
foreign import emptyDisposable :: Disposable
|
||||||
foreign import disposeAll :: Array Disposable -> Disposable
|
foreign import disposeAll :: Array Disposable -> Disposable
|
||||||
foreign import scheduleTasks :: Array Task -> Scheduler -> Disposable
|
foreign import scheduleTasks :: Array Task -> Scheduler -> Disposable
|
||||||
|
-- combinators
|
||||||
foreign import drain :: forall e a. Stream a -> EffStream e Unit
|
foreign import drain :: forall e a. Stream a -> EffStream e Unit
|
||||||
foreign import tapEvent :: forall e a. (a -> EffStream e Unit) -> Stream a -> Stream a
|
foreign import mergeConcurrently :: forall a. Int -> Stream (Stream a) -> Stream a
|
||||||
foreign import tapEnd :: forall e a. EffStream e Unit -> Stream a -> Stream a
|
foreign import mergeMapConcurrently :: forall a b. (a -> Stream b) -> Stream a -> Stream b
|
||||||
|
foreign import combine2 :: forall a b c. (a -> b -> c) -> Stream a -> Stream b -> Stream c
|
||||||
|
foreign import combine3 :: forall a b c d. (a -> b -> c -> d) -> Stream a -> Stream b -> Stream c -> Stream d
|
||||||
|
foreign import combine4 :: forall a b c d e. (a -> b -> c -> d -> e) -> Stream a -> Stream b -> Stream c -> Stream d -> Stream e
|
||||||
|
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
|
||||||
|
foreign import join :: forall a. Stream (Stream a) -> Stream a
|
||||||
|
foreign import flatMap :: forall a b. Stream a -> (a -> Stream b) -> Stream b
|
||||||
|
foreign import continueWith :: forall a. (Unit -> Stream a) -> Stream a -> Stream a
|
||||||
|
foreign import concat :: forall a. Stream a -> Stream a -> Stream a
|
||||||
|
foreign import merge :: forall a. Array (Stream a) -> Stream a
|
||||||
|
foreign import switch :: forall a. Stream (Stream a) -> Stream a
|
||||||
|
Loading…
Reference in New Issue
Block a user