Skip to content

gajus/babel-plugin-transform-function-composition

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

babel-plugin-transform-function-composition

Travis build status NPM version Canonical Code Style Twitter Follow

Transpiles function-bind call expressions to partially applied call expressions. Uses the invocation context to set the last parameter of the callee function.

Syntactically, the babel-plugin-syntax-function-bind also allows the reader to read the functions in left to right order of application, rather than reading from the innermost expression out.

  • Familiar with Ramda? This transpiler enables a syntactic sugar for pipe function.
  • Coming from Clojure? This transpiler enables a syntactic sugar for thread-first macro.

BIG WARNING: This is a proof-of-concept. See Motivation.

Example transpilation

Input:

apple
  ::foo('foo parameter 0', 'foo parameter 1')
  ::bar('bar parameter 0')
  ::baz('baz parameter 0');

Output:

baz(
  'baz parameter 0',
  bar(
    'bar parameter 0',
    foo(
      'foo parameter 0',
      'foo parameter 1',
      apple
    )
  )
);

Motivation

To make functional programming in JavaScript sweeter 🍧🍨🍦.

Participating

Help this proposal to get more attention by spreading the word (or retweet the original announcement)!

Participate in a reddit discussion to share your thoughts and suggestions.

ECMAScript Proposal

There is no active proposal for this functionality.

I am looking for feedback. If there is sufficient interest, I will proceed with a proposal.

Difference from This-Binding Syntax proposal

ECMAScript This-Binding Syntax proposal introduces a new operator :: which performs this binding and method extraction, i.e.

The following input:

foo::bar()::baz()

Becomes the following output:

var _context;

(_context = (_context = foo, bar).call(_context), baz).call(_context);

babel-plugin-transform-function-composition uses the :: operator to create a partially applied function such that the left hand side of the operator is set as the the first parameter to the target function on the right hand side, i.e.

The following input:

foo::bar()::baz()

Becomes the following output:

baz(bar(foo));

Difference from the Pipeline Operator proposal

ECMAScript Pipeline Operator proposal introduces a new operator |> which is a syntactic sugar on a function call with a single argument. In other words, sqrt(64) is equivalent to 64 |> sqrt.

The biggest difference between :: and |> is that the latter permits only a single parameter.

NOTE: There is no Babel transpiler for the Pipeline Operator proposal. See tc39/proposal-pipeline-operator#33

The reason for using the :: syntax

The :: syntax conflicts with the This-Binding Syntax proposal. However, at the time of writing this This-Binding Syntax proposal remains in stage 0 without an active champion (see What's keeping this from Stage 1?). In the mean time, the syntax support has been added to Babel (babel-plugin-syntax-function-bind).

The reason for choosing the :: operator for this proposal is to enable early adoption of this functionality.

NOTE: Should this implementation develop into a proposal, it is possible that an alternative syntax will be proposed (e.g. ->>).

Usage examples

Implementing Bluebird API

Bluebird is a promise library that provides non-standard utilities used to abstract common Promise operations.

Here is an example of using Promise.map and Promise.filter:

import Promise from 'bluebird';

Promise
  .resolve([
    'foo',
    'bar',
    'baz'
  ])
  .map((currentValue) => {
    return currentValue.toUpperCase();
  })
  .filter((currentValue) => {
    return currentValue.indexOf('B') === 0;
  });

Bluebird achieves this by providing a custom implementation of Promise object. This can be achieved by adding map and filter functions to the native Promise.prototype.

Note: Augmenting the built-in prototype is considered an anti-pattern. This approach is mentioned only for the sake of completeness of the example.

You can use :: to achieve an equivalent composition:

const map = async (callback, promise) => {
  const values = await promise;

  return values.map(callback);
};

const filter = async (callback, promise) => {
  const values = await promise;

  return values.filter(callback);
};

Promise
  .resolve([
    'foo',
    'bar',
    'baz'
  ])
  ::map((currentValue) => {
    return currentValue.toUpperCase();
  })
  ::filter((currentValue) => {
    return currentValue.indexOf('B') === 0;
  });

In both cases, the result of the operation is:

[
  'BAR',
  'BAZ'
]

Bluebird is heavy dependency (31Kb). Using function composition you have implemented equivalent functionality without the bundle size overhead.

Composing Ramda functions

Ramda is a functional flavor utility library. Ramda is designed to enable build functions as sequences of simpler functions, each of which transforms the data and passes it along to the next.

Here is an example of using R.pipe to perform left-to-right function composition:

import {
  assocPath,
  pipe
} from 'ramda';

pipe(
  assocPath(['repository', 'type'], 'git'),
  assocPath(['repository', 'url'], 'https://github.com/gajus/babel-plugin-transform-function-composition')
)({
  name: 'babel-plugin-transform-function-composition'
})

You can use :: to achieve an equivalent composition:

import {
  assocPath
} from 'ramda';

({
  name: 'babel-plugin-transform-function-composition'
})
  ::assocPath(['repository', 'type'], 'git')
  ::assocPath(['repository', 'url'], 'https://github.com/gajus/babel-plugin-transform-function-composition');

In both cases, the result of the operation is:

{
  name: 'babel-plugin-transform-function-composition',
  repository: {
    type: 'git',
    url: 'https://github.com/gajus/babel-plugin-transform-function-composition'
  }
}

About

Syntactic sugar 🍧🍨🍦 for easy to read function composition. πŸ¦„

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published