A Fallback for the JavaScript Pipeline Operator
Tags: javascript, functional, composition
Published: 2021-01-06 12:11:58

## Background

As I've also mentioned in my [Introduction to Fluture](https://dev.to/avaq/fluture-a-functional-alternative-to-promises-21b), there's a tc39 [proposal for inclusion of a "pipeline operator"](https://github.com/tc39/proposal-pipeline-operator) into the JavaScript language.

This is great news for functionally-minded libraries such as [Ramda](https://ramdajs.com/), [Sanctuary](https://sanctuary.js.org/), [Fluture](https://github.com/fluture-js/Fluture), and many more. It also makes some vanilla JavaScript nicer, for example:

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

const answer = String(
  Math.floor(
    Math.sqrt(
      JSON.parse(input).data
    )
  )
);
```

Becomes

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

const answer = input
  |> JSON.parse
  |> (x => x.data)
  |> Math.sqrt
  |> Math.floor
  |> String;
```

Pretty neat, right? Well, sadly [not everyone agrees](https://github.com/tc39/proposal-pipeline-operator/issues/167). 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](https://github.com/tc39/proposal-pipeline-operator/issues/167#issuecomment-645294563), [here](https://github.com/tc39/proposal-pipeline-operator/issues/167#issuecomment-647334570), [here in more depth](https://github.com/tc39/proposal-pipeline-operator/issues/167#issuecomment-764496429), and *very* extensively over [here](https://github.com/tc39/proposal-pipeline-operator/issues/233).

## 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:

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

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

Very similar to the pipeline example from above! :)

This is how it can be achieved:

```js
const key = Symbol('pipe');

global.o = key;

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

Object.defineProperty(Object.prototype, key, {value: pipe});
```

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.
3. `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).