Function composition syntax

Isiah Meadows isiahmeadows at
Wed Sep 28 23:45:55 UTC 2016


On Wed, Sep 7, 2016, 11:05 Mike Samuel <mikesamuel at> wrote:

> On Wed, Sep 7, 2016 at 10:10 AM, Isiah Meadows <isiahmeadows at>
> wrote:
> > I would like to see a function composition operator make it into the
> > language. Currently, there is:
> >
> > - Lodash: `_.flow` and `_.flowRight` (lodash/fp alias: `_.compose` and
> > `_.composeRight`)
> > - Underscore: `_.compose`
> > - Ramda: `R.compose`
> > - Tons of npm modules:
> > - Numerous manual implementations
> >
> > Function composition could be far more efficiently implemented in the
> > engine, in ways not possible at the language level:
> >
> > 1. They can create pipelines to optimize multiple composition chains
> > together.
> > 2. They can avoid most of the closure allocation cost internally.
> > 3. The returned functions can internally use a separate call path to
> avoid
> > some of the [[Call]] boilerplate when called and when calling the
> functions
> > themselves (you don't need to verify twice).
> >
> > Here's what I propose: a new infix operator `>=>` (operator and direction
> > can change) for composing two functions. It would do the following:
> What is the advantage of an operator over a static `Function.compose`
> or `Function.compose{Left,Right}`?

See my section on why I suggested the operator instead of a function. One
of the biggies is that the engine can statically optimize it, including
inline arrow functions (no function object needs created). Another that I
didn't list is that you are guaranteed 2 operands, so you can't "compose" 1
function. Also, fewer parentheses are always better IMO.

Oh, and there's a reason most `compose` functions accept multiple
arguments: it would become way too unwieldy with all the parentheses

> > 1. Verify both operands are callable.
> What should happen if you try to compose something that only supports
> [[Call]] with something that only supports [[Construct]]?  For
> example, one might try to compose Object.freeze with a constructor to
> get a producer of frozen instances.

I can adjust my proposal accordingly to cover "construct first when called
as constructor". I missed that use case, but it's easy to fix.

> > 2. Create a callable-only function that calls its left operand with the
> > original arguments and `this`, then calling its right operand with the
> > result and the same `this`.
> > 3. Sets its length to the left operand.
> > 4. Return the new function.
> Is the new function strict only when both operands are strict or when
> either is strict?
> Or should it depend on the scope in which the operator appears?
> When the left operand is non-strict, is the composition the caller of
> the left operand?

Composed functions would be similar to bound functions. So it shouldn't
make a difference.

A transpiler can transform `f >=> g` directly to `function () { return, f.apply(this, arguments)) }` (mod type checks).

> > The reason I suggested an operator instead of a function:
> >
> > 1. Fewer parentheses is always a plus.
> > 2. It allows engines to statically optimize functions in the middle
> (avoid
> > an extra function allocation), like with `f >=> x => console.log("x:" +
> x)`.
> This seems doable optimistically with Function.compose, though you'd
> have to back out when Function.compose is assigned.

Engines don't usually make optimistic assumptions like that on the first
pass, much less the first 100 or so (it takes thousands for V8's inliner to
trigger, for example). They would have to cut a very specific special case
for this (they don't even do that for `bind` IIRC), which I don't see as

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the es-discuss mailing list