Coder Social home page Coder Social logo

williamstephens / proposal-pipeline-operator Goto Github PK

View Code? Open in Web Editor NEW

This project forked from tc39/proposal-pipeline-operator

0.0 1.0 0.0 65 KB

A proposal for adding the simple-but-useful pipeline operator to JavaScript.

Home Page: http://tc39.github.io/proposal-pipeline-operator/

Shell 11.82% HTML 88.18%

proposal-pipeline-operator's Introduction

ESNext Proposal: The Pipeline Operator

This proposal introduces a new operator |> similar to F#, OCaml, Elixir, Elm, Julia, Hack, and LiveScript, as well as UNIX pipes. It's a backwards-compatible way of streamlining chained function calls in a readable, functional manner, and provides a practical alternative to extending built-in prototypes.

Introduction

The pipeline operator is essentially a useful syntactic sugar on a function call with a single argument. In other words, sqrt(64) is equivalent to 64 |> sqrt.

This allows for greater readability when chaining several functions together. For example, given the following functions:

function doubleSay (str) {
  return str + ", " + str;
}
function capitalize (str) {
  return str[0].toUpperCase() + str.substring(1);
}
function exclaim (str) {
  return str + '!';
}

...the following invocations are equivalent:

let result = exclaim(capitalize(doubleSay("hello")));
result //=> "Hello, hello!"

let result = "hello"
  |> doubleSay
  |> capitalize
  |> exclaim;

result //=> "Hello, hello!"

Functions with Multiple Arguments

The pipeline operator does not need any special rules for functions with multiple arguments; JavaScript already has ways to handle such cases.

For example, given the following functions:

function double (x) { return x + x; }
function add (x, y) { return x + y; }

function boundScore (min, max, score) {
  return Math.max(min, Math.min(max, score));
}

...you can use an arrow function to handle multi-argument functions (such as add):

let person = { score: 25 };

let newScore = person.score
  |> double
  |> (_ => add(7, _))
  |> (_ => boundScore(0, 100, _));

newScore //=> 57

// As opposed to: let newScore = boundScore( 0, 100, add(7, double(person.score)) )

Note: The use of underscore _ is not required; it's just an arrow function, so you can use any parameter name you like.

As you can see, because the pipe operator always pipes a single result value, it plays very nicely with the single-argument arrow function syntax. Also, because the pipe operator's semantics are pure and simple, it could be possible for JavaScript engines to optimize away the arrow function.

Use of await

The pipeline operator allows the result of a Promise-returning function to be awaited as follows:

x |> await f

which is the equivalent of

await f(x)

This is to allow you to await the result of an asynchronous function and pass it to the next function from within a function pipeline, as follows:

const userAge = userId |> await fetchUserById |> getAgeFromUser

which is the equivalent of

const userAge = getAgeFromUser(await fetchUserById(userId))

Usage with ? partial application syntax

If the partial application proposal (currently a stage 1 proposal) gets accepted, the pipeline operator would be even easier to use. We would then be able to rewrite the previous example like so:

let person = { score: 25 };

let newScore = person.score
  |> double
  |> add(7, ?)
  |> boundScore(0, 100, ?);

newScore //=> 57

Motivating Examples

Object Decorators

Mixins via Object.assign are great, but sometimes you need something more advanced. A decorator function is a function that receives an existing object, adds to it (mutative or not), and then returns the result.

Decorator functions are useful when you want to share behavior across multiple kinds of objects. For example, given the following decorators:

function greets (person) {
  person.greet = () => `${person.name} says hi!`;
  return person;
}
function ages (age) {
  return function (person) {
    person.age = age;
    person.birthday = function () { person.age += 1; };
    return person;
  }
}
function programs (favLang) {
  return function (person) {
    person.favLang = favLang;
    person.program = () => `${person.name} starts to write ${person.favLang}!`;
    return person;
  }
}

...you can create multiple "classes" that share one or more behaviors:

function Person (name, age) {
  return { name: name } |> greets |> ages(age);
}
function Programmer (name, age) {
  return { name: name }
    |> greets
    |> ages(age)
    |> programs('javascript');
}

Validation

Validation is a great use case for pipelining functions. For example, given the following validators:

function bounded (prop, min, max) {
  return function (obj) {
    if ( obj[prop] < min || obj[prop] > max ) throw Error('out of bounds');
    return obj;
  };
}
function format (prop, regex) {
  return function (obj) {
    if ( ! regex.test(obj[prop]) ) throw Error('invalid format');
    return obj;
  };
}

...we can use the pipeline operator to validate objects quite pleasantly:

function createPerson (attrs) {
  attrs
    |> bounded('age', 1, 100)
    |> format('name', /^[a-z]$/i)
    |> Person.insertIntoDatabase;
}

Usage with Prototypes

Although the pipe operator operates well with functions that don't use this, it can still integrate nicely into current workflows:

import Lazy from 'lazy.js'

getAllPlayers()
  .filter( p => p.score > 100 )
  .sort()
|> _ => Lazy(_)
  .map( p => p.name )
  .take(5)
|> _ => renderLeaderboard('#my-div', _);

Mixins

"Real" Mixins have some syntax problems, but the pipeline operator cleans them up quite nicely. For example, given the following classes and mixins:

class Model {
  // ...
}
let Editable = superclass => class extends superclass {
  // ...
};
let Sharable = superclass => class extends superclass {
  // ...
};

... we can use the pipeline operator to create a new class that extends Model and mixes Editable and Sharable, with a more readable syntax:

// Before:
class Comment extends Sharable(Editable(Model)) {
  // ...
}
// After:
class Comment extends Model |> Editable |> Sharable {
  // ...
}

Real-world Use Cases

Check out the Example Use Cases wiki page to see more possibilities.

Related proposals

If you like this proposal, you will certainly like the proposal for easier partial application. Take a look and star if you like it!

You may also be interested in these separate proposals for a function composition operator:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.