Repository: mostjs/core
Pull request #80 (2017-06-19 12:00:24)

chore(concat): Remove concat
Remove concat.  The name "concat" conjures up a mental model of array concatenation, which can have completely nonsensical time semantics.  For example, what does *concatenating* mouse clicks onto mousemoves even mean?  It also seems to have no practical use outside of *data processing* applications, which are not the focus of `@most/core`.

Interestingly, `continueWith` seems to have a more reasonable mental model: start by observing event stream 1 and when it ends, start observing event stream 2.  To use the mouse click/move example from above, asking for all the mouse clicks that happen after some number of mouse moves, e.g. `continueWith(() => click(el), take(10, mousemove(el)))`, at least seems to conjure up a sensible mental image.

It's possible a similar operator is still useful, but with clearer time semantics, and a name that creates a mental image like "all the Ys that happen after all the Xs", like `continueWith` seems to do.

Frikki: I totally agree with that.
briancavalier: Gonna merge this after #79, since that seems like it will cause less of a rebase headache.

Patch:
diff --git a/packages/core/src/combinator/build.js b/packages/core/src/combinator/build.js
deleted file mode 100644
index d42699a..0000000
--- a/packages/core/src/combinator/build.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/** @license MIT License (c) copyright 2010-2016 original author or authors */
-/** @author Brian Cavalier */
-/** @author John Hann */
-
-import { now } from '../source/now'
-import { continueWith } from './continueWith'
-
-/**
- * @param {*} x value to prepend
- * @param {Stream} stream
- * @returns {Stream} new stream with x prepended
- */
-export const startWith = (x, stream) => concat(now(x), stream)
-
-/**
-* @param {Stream} left
-* @param {Stream} right
-* @returns {Stream} new stream containing all events in left followed by all
-*  events in right.  This *timeshifts* right to the end of left.
-*/
-export const concat = (left, right) => continueWith(() => right, left)
diff --git a/packages/core/src/combinator/startWith.js b/packages/core/src/combinator/startWith.js
new file mode 100644
index 0000000..74ede5a
--- /dev/null
+++ b/packages/core/src/combinator/startWith.js
@@ -0,0 +1,7 @@
+/** @license MIT License (c) copyright 2010-2017 original author or authors */
+
+import { now } from '../source/now'
+import { continueWith } from './continueWith'
+
+export const startWith = (x, stream) =>
+  continueWith(() => stream, now(x))
diff --git a/packages/core/src/index.js b/packages/core/src/index.js
index 3d4e501..72c3cdf 100644
--- a/packages/core/src/index.js
+++ b/packages/core/src/index.js
@@ -35,9 +35,8 @@ export const scan = curry3(_scan)
 // -----------------------------------------------------------------------
 // Extending
 
-import { concat as _concat, startWith as _startWith } from './combinator/build'
+import { startWith as _startWith } from './combinator/startWith'
 
-export const concat = curry2(_concat)
 export const startWith = curry2(_startWith)
 
 // -----------------------------------------------------------------------
diff --git a/packages/core/src/index.js.flow b/packages/core/src/index.js.flow
index 0f4d5f3..0fda667 100644
--- a/packages/core/src/index.js.flow
+++ b/packages/core/src/index.js.flow
@@ -21,8 +21,6 @@ declare export function newStream <A> (run: RunStream<A>): Stream<A>
 
 declare export function startWith <A> (a: A, s: Stream<A>): Stream<A>
 declare export function startWith <A> (a: A): (s: Stream<A>) => Stream<A>
-declare export function concat <A> (s1: Stream<A>, s2: Stream<A>): Stream<A>
-declare export function concat <A> (s1: Stream<A>): (s2: Stream<A>) => Stream<A>
 
 declare export function periodic (period: Period): Stream<void>
 
diff --git a/packages/core/type-definitions/combinator/build.d.ts b/packages/core/type-definitions/combinator/startWith.d.ts
similarity index 51%
rename from packages/core/type-definitions/combinator/build.d.ts
rename to packages/core/type-definitions/combinator/startWith.d.ts
index 8295c22..209f572 100644
--- a/packages/core/type-definitions/combinator/build.d.ts
+++ b/packages/core/type-definitions/combinator/startWith.d.ts
@@ -2,6 +2,3 @@ import { Stream } from '@most/types';
 
 export function startWith<A>(value: A, stream: Stream<A>): Stream<A>;
 export function startWith<A>(value: A): (stream: Stream<A>) => Stream<A>;
-
-export function concat<A>(leftStream: Stream<A>, rightStream: Stream<A>): Stream<A>;
-export function concat<A>(leftStream: Stream<A>): (rightStream: Stream<A>) => Stream<A>;
diff --git a/packages/core/type-definitions/most.d.ts b/packages/core/type-definitions/most.d.ts
index 68fbc45..254f722 100644
--- a/packages/core/type-definitions/most.d.ts
+++ b/packages/core/type-definitions/most.d.ts
@@ -2,7 +2,6 @@ export * from './runEffects';
 export * from './PropagateTask';
 
 export * from './combinator/applicative';
-export * from './combinator/build';
 export * from './combinator/chain';
 export * from './combinator/combine';
 export * from './combinator/concatMap';
@@ -18,6 +17,7 @@ export * from './combinator/promises';
 export * from './combinator/sample';
 export * from './combinator/scan';
 export * from './combinator/slice';
+export * from './combinator/startWith';
 export * from './combinator/switch';
 export * from './combinator/timeslice';
 export * from './combinator/transform';


Test patch:
diff --git a/packages/core/test/build-test.js b/packages/core/test/build-test.js
deleted file mode 100644
index bff71d4..0000000
--- a/packages/core/test/build-test.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { describe, it } from 'mocha'
-import { eq } from '@briancavalier/assert'
-
-import { startWith, concat } from '../src/combinator/build'
-import { now } from '../src/source/now'
-import { empty } from '../src/source/empty'
-import { delay } from '../src/combinator/delay'
-
-import { collectEventsFor, makeEventsFromArray } from './helper/testEnv'
-
-const sentinel = { value: 'sentinel' }
-
-const assertSingleEvent = value => events => {
-  eq(1, events.length)
-  eq({ time: 0, value }, events[0])
-}
-
-describe('build', function () {
-  describe('startWith', function () {
-    it('should return a stream containing item as head', function () {
-      const s = startWith(sentinel, empty())
-
-      return collectEventsFor(1, s)
-        .then(assertSingleEvent(sentinel))
-    })
-  })
-
-  describe('concat', function () {
-    it('should return a stream containing items from both streams in correct order', function () {
-      const dt = 1
-      const s1 = delay(dt, makeEventsFromArray(1, [1, 2]))
-      const s2 = makeEventsFromArray(1, [3, 4])
-
-      return collectEventsFor(dt + 3, concat(s1, s2))
-        .then(eq([
-            { time: 1, value: 1 },
-            { time: 2, value: 2 },
-            { time: 2, value: 3 },
-            { time: 3, value: 4 }]
-        ))
-    })
-
-    it('should satisfy left identity', function () {
-      const s = concat(now(sentinel), empty())
-
-      return collectEventsFor(1, s)
-        .then(assertSingleEvent(sentinel))
-    })
-
-    it('should satisfy right identity', function () {
-      const s = concat(empty(), now(sentinel))
-
-      return collectEventsFor(1, s)
-        .then(assertSingleEvent(sentinel))
-    })
-  })
-})
diff --git a/packages/core/test/combinator/chain-test.js b/packages/core/test/combinator/chain-test.js
index ab55df2..4ba3df3 100644
--- a/packages/core/test/combinator/chain-test.js
+++ b/packages/core/test/combinator/chain-test.js
@@ -4,7 +4,7 @@ import { spy } from 'sinon'
 
 import { chain, join } from '../../src/combinator/chain'
 import { delay } from '../../src/combinator/delay'
-import { concat } from '../../src/combinator/build'
+import { startWith } from '../../src/combinator/startWith'
 import { take } from '../../src/combinator/slice'
 import { drain } from '../helper/observe'
 import { now } from '../../src/source/now'
@@ -78,7 +78,7 @@ describe('join', function () {
   })
 
   it('should dispose inner stream immediately', function () {
-    const s = now(concat(now(1), never()))
+    const s = now(startWith(1, never()))
     return drain(take(1, join(s))).then(() => assert(true))
   })
 
diff --git a/packages/core/test/combinator/concatMap-test.js b/packages/core/test/combinator/concatMap-test.js
index 8fec505..3101402 100644
--- a/packages/core/test/combinator/concatMap-test.js
+++ b/packages/core/test/combinator/concatMap-test.js
@@ -5,7 +5,7 @@ import { spy } from 'sinon'
 import { assertSame } from '../helper/stream-helper'
 
 import * as concatMap from '../../src/combinator/concatMap'
-import { concat } from '../../src/combinator/build'
+import { startWith } from '../../src/combinator/startWith'
 import { take } from '../../src/combinator/slice'
 import { drain } from '../helper/observe'
 import { now } from '../../src/source/now'
@@ -84,7 +84,7 @@ describe('concatMap', function () {
   })
 
   it('should dispose inner stream immediately', function () {
-    const s = now(concat(now(1), never()))
+    const s = now(startWith(1, never()))
 
     return drain(take(1, concatMap.concatMap(identity, s)))
   })
diff --git a/packages/core/test/startWith-test.js b/packages/core/test/startWith-test.js
new file mode 100644
index 0000000..f1a5826
--- /dev/null
+++ b/packages/core/test/startWith-test.js
@@ -0,0 +1,17 @@
+import { describe, it } from 'mocha'
+import { eq } from '@briancavalier/assert'
+
+import { startWith } from '../src/combinator/startWith'
+import { empty } from '../src/source/empty'
+
+import { collectEventsFor } from './helper/testEnv'
+
+describe('startWith', function () {
+  it('should return a stream containing item as head', function () {
+    const value = Math.random()
+    const s = startWith(value, empty())
+
+    return collectEventsFor(1, s)
+      .then(eq([{ time: 0, value }]))
+  })
+})