Skip to content
Eugene Lazutkin edited this page Sep 18, 2024 · 17 revisions

defs is a module, which defines all special constants and convenience methods used in chain().

Its properties are mixed in or otherwise duplicated and re-exported in other modules like chain().

The example of usage:

import {none} from 'stream-chain/defs.js';
// const {none} = require('stream-chain/defs.js');

Special return values for functions

none

none is a special value, which terminates the chain and produces no value.

// import chain, {none} from 'stream-chain';
// import {chain, none} from 'stream-chain';
// const {chain, none} = require('stream-chain');

import chain from 'stream-chain';
import {none} from 'stream-chain/defs.js';

// a filter
dataSource
  .pipe(chain([
    x => x * x,
    x => x % 2 == 0 ? none : x,
    x => 2 * x + 1
  ]));
// skips even values

// if dataSource produces: 1, 2, 3
// then the result will be: 3, 19

This is the definition of none:

const none = Symbol.for('object-stream.none');

stop

stop is a special value, which terminates the chain, produces no value, and stops further processing. Usually, it is used to terminate potentially infinite generators.

// import chain, {stop} from 'stream-chain';
// const {chain, stop} = require('stream-chain');

import chain from 'stream-chain';
import {stop} from 'stream-chain/defs.js';

chain([
  function* () { for (let i = 0; ; ++i) yield i; }
  n => n > 1000 ? stop : n,
]);
// a stream produces numbers from 0 to 1000 inclusively

This is the definition of stop:

const stop = Symbol.for('object-stream.stop');

Important: stop works only within function chain segments created by gen() or fun(). The native streams do not support this feature treating it as none.

Final values

Helper functions to mark values as final. A final value stops the chain and returns its value.

Important: this feature works only within function chain segments created by gen() or fun(). The native streams do not support this feature treating its payload as a regular value.

finalSymbol

Used internally to mark a value as a final value. The definition:

const finalSymbol = Symbol.for('object-stream.final');

finalValue(value)

This is a helper factory function, which can be used in by chained functions. It returns a special value, which terminates the chain and uses the passed value as the result of the chain.

// import chain, {finalValue} from 'stream-chain';
// const {chain, finalValue} = require('stream-chain');

import chain from 'stream-chain';
import {finalValue} from 'stream-chain/defs.js';

dataSource
  .pipe(chain([[
    x => x * x,
    x => finalValue(x),
    x => 2 * x + 1 // will be skipped
  ]]));

// if dataSource produces: 1, 2, 3
// then the result will be: 1, 4, 9

isFinalValue(value)

isFinalValue(value) is a companion to finalValue(). It checks if a value was marked as final returning a standard truthy/falsy result.

// import chain, {finalValue, isFinalValue} from 'stream-chain';
// const {chain, finalValue, isFinalValue} = require('stream-chain');

import chain from 'stream-chain';
import {finalValue, isFinalValue} from 'stream-chain/defs.js';

dataSource
  .pipe(chain([
    x => {
      let result = finalValue(x);
      // ...
      if (isFinalValue(result)) {
        // do something
      } else {
        // do something else
      }
      // ...
    },
    // the rest of pipeline
  ]));

getFinalValue(value)

getFinalValue(value) is a companion to finalValue() and isFinalValue(). Its argument should be a wrapped final value. Its return will be an unwrapped value.

// import chain, {finalValue, isFinalValue, getFinalValue} from 'stream-chain';
// const {chain, finalValue, isFinalValue, getFinalValue} = require('stream-chain');

import chain from 'stream-chain';
import {finalValue, isFinalValue, getFinalValue} from 'stream-chain/defs.js';

dataSource
  .pipe(chain([
    x => {
      let result = finalValue(42);
      // ...
      if (isFinalValue(result)) {
        const value = getFinalValue(result);
        console.log(value);
        // do something
      } else {
        console.log(result);
        // do something else
      }
      // ...
    },
    // the rest of pipeline
  ]));

Many values

The right way to return multiple values is to use a generator function. Sometimes it is not possible to do that for some reason, e.g., because of performance considerations or for simplicity.

That's why there are helper functions that allow you to return multiple values from regular functions.

The obvious downside is that a generator function sends each value down the chain as soon as it is produced, while a regular function will accumulate the values in an array before they are sent down the chain as multiple values.

The other reason for this facility is historical: the stream-chain library was originally designed when generators were not available. Now it can be used for backward compatibility.

manySymbol

Used internally to mark a value as multiple values. The definition:

const manySymbol = Symbol.for('object-stream.many');

many(values)

This is a helper factory function, which is used to wrap arrays to be interpreted as multiple values returned from a function.

// import chain, {many} from 'stream-chain';
// const {chain, many} = require('stream-chain');

import chain from 'stream-chain';
import {many} from 'stream-chain/defs.js';

dataSource
  .pipe(chain([x => many([x, x + 1, x + 2])]));

// if dataSource produces: 1, 5
// then the result will be: 1, 2, 3, 5, 6, 7

isMany(value)

isMany(value) is a companion to many(). It checks if a value was marked as multiple values returning a standard truthy/falsy result.

// import chain, {many, isMany} from 'stream-chain';
// const {chain, many, isMany} = require('stream-chain');

import chain from 'stream-chain';
import {many, isMany} from 'stream-chain/defs.js';

dataSource
  .pipe(chain([
    x => {
      let result = many([x, x + 1]);
      // ...
      if (isMany(result)) {
        // do something
      } else {
        // do something else
      }
      // ...
    },
    // the rest of pipeline
  ]));

getManyValues(value)

getManyValues(value) is a companion to many() and isMany(). Its argument should be a wrapped multiple value. Its return will be an unwrapped value (an array of values).

// import chain, {many, isMany, getManyValues} from 'stream-chain';
// const {chain, many, isMany, getManyValues} = require('stream-chain');

import chain from 'stream-chain';
import {many, isMany, getManyValues} from 'stream-chain/defs.js';

dataSource
  .pipe(chain([
    x => {
      let result = many([1, 42, 99]);
      // ...
      if (isMany(result)) {
        const values = getManyValues(result);
        console.log(values);
        // do something
      } else {
        console.log(result);
        // do something else
      }
      // ...
    },
    // the rest of pipeline
  ]));

Flushable functions

Helper functions to mark a function as being flushable. A flushable function will be called when the end of the stream is reached. When it happens, it will be called with a special value none (see above). This value is never used in the normal course of processing. When it happens, the function should produce delayed values, if any.

Unmarked functions are not flushable and will not be called when the end of the stream is reached.

flushableSymbol

Used internally to mark a function as being flushable. The definition:

const flushSymbol = Symbol.for('object-stream.flush');

flushable(fn, final = null)

This function marks a function as being flushable. Its arguments are:

  • fn is the function to be marked as flushable.
  • final is an optional function that will be called when the end of the stream is reached with no arguments. Otherwise, fn will be called with a single argument none (see above). Defaults to null.

It returns the augmented/modified function.

// import chain, {none, flushable} from 'stream-chain';
// const {chain, none, flushable} = require('stream-chain');

import chain from 'stream-chain';
import {none, flushable} from 'stream-chain/defs.js';

let acc = 0;

dataSource
  .pipe(chain([
    flushable(x => {
      if (x === none) {
        return acc; // return the accumulated value
      }
      acc += x;
      return none; // produce no result
    })
  ]));
// if dataSource produces: 1, 2, 3
// then the result will be: 6

The same example can be reformulated like that:

let acc = 0;

dataSource
  .pipe(chain([
    flushable(
      x => void acc += x,
      () => acc
    )
  ]));
// if dataSource produces: 1, 2, 3
// then the result will be: 6

isFlushable(fn)

isFlushable(fn) is a companion to flushable(). It checks if a function is marked as flushable. It is mostly used internally.

Function lists

Helper functions to mark a function as being a wrapper for a list of functions. A function list is an array of functions used in chains. It is a feature used internally to optimize pipelines: when available a function list is inlined into the pipeline instead of using the wrapper function. It is used by gen and fun modules.

This optimization is used for performance reasons and is enabled by default.

In some cases this it can cause logical problems. For example, if you use finalValue() it will not work as expected if you use a function list. Essentially it will be globalized. If your intention was to confine the final value to a specific segment, use clearFunctionList() to suppress the optimization.

fListSymbol

Used internally to mark a function as being derived from a function list. The definition:

const fListSymbol = Symbol.for('object-stream.fList');

setFunctionList(value, fns)

This function marks a value as being derived from a function list. Its arguments are:

  • value is the value to be marked, usually a function.
  • fns is an array of functions.

It returns the augmented/modified value.

isFunctionList(fn)

This function checks if a value is derived from a function list. It is mostly used internally.

getFunctionList(fn)

This function extracts the function list from a value. It is mostly used internally.

clearFunctionList(fn)

In some cases we want to suppress the function list optimization and use the wrapper function as is. A good example would be final values scoped for a specific function list. This function clears the function list from a value suppressing the optimization.

It returns fn.

Example:

import {gen, clearFunctionList} from 'stream-chain';

// treated as a wrapper, the functions will be inlined
const p1 = gen(x => x + 1, x => x * x);

// treated as an opaque function
const p2 = clearFunctionList(gen(x => x + 1, x => x * x));

const c1 = gen(p1, p2);
// effectively the same as:
const c2 = gen(x => x + 1, x => x * x, p2);

Exceptions

Stop

Used to terminate the processing of a value by a pipeline. It can be thrown by any function in the pipeline.

Usually returning the stop value is more convenient than throwing it.

// import chain, {Stop} from 'stream-chain';
// const {chain, Stop} = require('stream-chain');

import chain from 'stream-chain';
import {Stop} from 'stream-chain/defs.js';

chain([
  function* () { for (let i = 0; ; ++i) yield i; }
  n => {
    if (n > 1000) throw new Stop();
    return n;
  }
]);
// a stream produces numbers from 0 to 1000 inclusively