Pipe operator for JavaScript

# David White (7 years ago)

Piping is useful when composing many small pure functions together, one problem with this is the evaluation needs to be read from the inner most expression value in reverse to the outer most function call. Given the following statement we can see that it would be read as 'call math round then call math square then pass in PI', whereas the order of execution is in reverse.

const myNumber = Math.round(Math.sqrt(Math.PI));

Unix has long provided a piping mechanism to achieve that, for example: ps aux | grep node, as well as F# and the Elixir programming language, both provide the |> pipe greater-than operator which is very readable.

My proposal would be to use the slightly more verbose |> pipe-greater than syntax, it provides a convenient direction of travel for the expression on the left side, into the expression on the right, F# also provides a <|, which pipes in the opposite direction though I’ve not really seen very good use cases for this operator.

const myNumber = Math.PI |> Math.sqrt |> Math.round;

The left side of the operator should always be a primitive data type or data structure, these could be any of the following: Boolean, Null, undefined, Number, String, Symbol or Object. Since functions are standard objects they can be passed in as the initial value, as long as the function on the right handles the calling on that function.

It also provides a way to either log, process, or do anything with the data from the last expression on the left at various stages of the execution without adding additional brackets in and around the calls, for example.

function logger (callback) {
  return function (value) {
    callback(value);
    return value;
  };
}

Math.PI
  |> logger(console.log);
  |> Math.sqrt
  |> logger(console.log);
  |> Math.round
  |> logger(console.log);

While a slightly contrived example as we certainly wouldn't write code that looks like:

logger(Math.round(logger(Math.sqrt(logger(Math.PI)))));

We would instead assign to variables at each stage of execution:

const PI = Math.PI;
logger(PI);

const squaredPI = Math.sqrt(PI);
logger(squaredPI);

const roundedSquaredPI = Math.round(squaredPI);
logger(roundedSquaredPI);

However with this clarity we have unfortunately had to create additional constants within this lexical block, whereas simple value passing between pure functions provides a very clean and readable approach and allows easier updates in the future if we wanted to add additional processing within the chain, for example Express middleware composition.

Would love to hear some thoughts on this?

David

# Logan Smyth (7 years ago)

There's been talk of this before here and in gilbert/es-pipeline-operator among other places. Most recently this was posted, which sounds like promising progress: gilbert/es-pipeline-operator#33

# David White (7 years ago)

Oh apologies for not taking a few moments to use the search facility. The linked github sounds extremely promising and certainly captures far more use cases than I initially intended.

Thanks Logan.