A Fallback for the JavaScript Pipeline Operator

avaq

Aldwin Vlasblom

Posted on January 6, 2021

A Fallback for the JavaScript Pipeline Operator

Background

As I've also mentioned in my Introduction to Fluture, there's a tc39 proposal for inclusion of a "pipeline operator" into the JavaScript language.

This is great news for functionally-minded libraries such as Ramda, Sanctuary, Fluture, and many more. It also makes some vanilla JavaScript nicer, for example:

const input = '{"data": 1765}';

const answer = String(
  Math.floor(
    Math.sqrt(
      JSON.parse(input).data
    )
  )
);
Enter fullscreen mode Exit fullscreen mode

Becomes

const input = '{"data": 1765}';

const answer = input
  |> JSON.parse
  |> (x => x.data)
  |> Math.sqrt
  |> Math.floor
  |> String;
Enter fullscreen mode Exit fullscreen mode

Pretty neat, right? Well, sadly not everyone agrees. And it is because of this disagreement that the operator has still not made it into the language. What's worse, chances are that when it does make it -- if it does -- it will be in a form that is not half as useful for functional programming. I have argued as to why here, here, here in more depth, and very extensively over here.

The Fallback

That brings me to a small idea I had that allows us to write code much like the above, but in today's JavaScript. You can use it now, while waiting for a consensus to be reached, and in the future, in case the committee decides against the functional variant of the operator. This is what it looks like:

const input = '{"data": 1765}';

const answer = input
  [o] (JSON.parse)
  [o] (x => x.data)
  [o] (Math.sqrt)
  [o] (Math.floor)
  [o] (String);
Enter fullscreen mode Exit fullscreen mode

Very similar to the pipeline example from above! :)

This is how it can be achieved:

const key = Symbol('pipe');

global.o = key;

function pipe(f){ return f(this) };

Object.defineProperty(Object.prototype, key, {value: pipe});
Enter fullscreen mode Exit fullscreen mode

Let's break this down.

  1. Symbol('pipe'): We define a unique symbol. We can use this to mutate existing objects without stepping on anyone's toes.
  2. global.o: This is an optional convenience. In the browser, this should be window.o. But you may just as well export const o instead and import it where needed, so not to pollute the global scope. I chose o as the name because it looks a bit like the mathematical composition operator (∘).
  3. function pipe: All that the (simple) pipe operator does is to apply a function to its context.
  4. Object.defineProperty(...): We attach our pipe method to the Object prototype using our symbol as a key. This immediately retrofits almost all possible values (all the ones that inherit from Object) with our pipeline capabilities.

Alright, well, that's all there is to it. Let's hope that we won't have to use this trick (much longer).

💖 💪 🙅 🚩
avaq
Aldwin Vlasblom

Posted on January 6, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Javascript: Functional Composition
javascript Javascript: Functional Composition

September 5, 2022

JS Basics: Higher Order Functions
basics JS Basics: Higher Order Functions

August 17, 2020