Function composition syntax
interesting proposal!
Here's what I propose: a new infix operator
>=>
(operator and direction can change) for composing two functions.
Sweet, reminds me of Kleisli composition in Haskell hackage.haskell.org/package/base-4.9.0.0/docs/Control-Monad.html#v:-62--61--62- (which does something different to functions though).
- It allows engines to statically optimize functions in the middle (avoid an extra function allocation), like with
f >=> x => console.log("x:" + x)
.
I don't understand that one. Wouldn't x => console.log("x:" + f(x))
be
optimised better (and also be easier to read)?
- It can simplify the internal model some to deal with a binary pair instead of an array, especially when pipelining gets involved.
- Composition isn't usually combined as a function in JS.
Can you clarify what you mean with this?
My questions would be
- What precedence would the operator have? Clearly something between member access and assignment, but what exactly? Particularly interesting cases: f >=> g (x) f >=> p ? g : h f >=> x => x >=> g
- Do we also need a partial application operator to make this syntax useful? I guess the discussions from tc39/proposal-bind-operator#35 and tc39/proposal-bind-operator#26 are relevant here.
Kind , Bergi
Inline.
On Wed, Sep 7, 2016, 10:44 Bergi <a.d.bergi at web.de> wrote:
Hi, interesting proposal!
Here's what I propose: a new infix operator
>=>
(operator and direction can change) for composing two functions.Sweet, reminds me of Kleisli composition in Haskell
hackage.haskell.org/package/base-4.9.0.0/docs/Control-Monad.html#v:-62--61--62- (which does something different to functions though).
That was an inspiration, but that adds to the list of reasons why I would be okay with a different operator.
- It allows engines to statically optimize functions in the middle (avoid an extra function allocation), like with
f >=> x => console.log("x:" + x)
.I don't understand that one. Wouldn't
x => console.log("x:" + f(x))
be optimised better (and also be easier to read)?
Yes, but it was a trivial example just for the sake of example.
- It can simplify the internal model some to deal with a binary pair instead of an array, especially when pipelining gets involved.
- Composition isn't usually combined as a function in JS.
Can you clarify what you mean with this?
Things like compose(compose, compose)
aren't frequently used in JS
(unlike Haskell's (.) . (.)
). I almost included that example, but I
couldn't get a wording I liked.
My questions would be
- What precedence would the operator have? Clearly something between member access and assignment, but what exactly? Particularly interesting cases: f >=> g (x) f >=> p ? g : h f >=> x => x >=> g
I intentionally left precedence out (other than implying it's chainable left to right), to avoid complicating the initial presentation.
- Do we also need a partial application operator to make this syntax useful?
I guess the discussions from tc39/proposal-bind-operator#35 and tc39/proposal-bind-operator#26 are relevant here.
It would help, but no, that's not required. Arrow functions can help in the meantime, and may be more readable, and sometimes required, in some cases, since it's more explicit.
(It's come up before, independently: esdiscuss.org/topic/syntax-sugar-for-partial-application)
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 samethis
.
IMHO, the order seems wrong. Not sure if programming languages do it differently, but in math composition works like this:
(f ∘ g)(x) = f(g(x))
So I think it would be more intuitive to use <=<
, which would call the right operand first, and then call the left operand with the result of the right one.
,Oriol
Inline.
On Wed, Sep 7, 2016, 11:05 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 10:10 AM, Isiah Meadows <isiahmeadows at gmail.com> 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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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
orFunction.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
otherwise.
- 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.
- 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 samethis
.
- Sets its length to the left operand.
- 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
g.call(this, f.apply(this, arguments)) }` (mod type checks).
The reason I suggested an operator instead of a function:
- Fewer parentheses is always a plus.
- 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
likely.
Somehow, this missed the list...
---------- Forwarded message --------- From: Isiah Meadows <impinball at gmail.com>
Date: Wed, Sep 7, 2016, 12:03 Subject: Re: Function composition syntax To: <mikesamuel at gmail.com>, <es-discuss at mozilla.org>
I was thinking in reverse order, but personally, I'm flexible on specifics like that.
On Wed, Sep 7, 2016 at 1:54 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
Somehow, this missed the list...
---------- Forwarded message --------- From: Isiah Meadows <impinball at gmail.com> Date: Wed, Sep 7, 2016, 12:03 Subject: Re: Function composition syntax To: <mikesamuel at gmail.com>, <es-discuss at mozilla.org>
I was thinking in reverse order, but personally, I'm flexible on specifics like that.
On Wed, Sep 7, 2016, 12:00 Isiah Meadows <isiahmeadows at gmail.com> wrote:
Inline.
On Wed, Sep 7, 2016, 11:05 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 10:10 AM, Isiah Meadows <isiahmeadows at gmail.com> 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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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
orFunction.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.
Thanks for answering. Part of the source of my confusion might be that I didn't see a link to any proposal. The only link I saw in the OP was a link to an NPM list of function composition implementations.
I'm unclear on why an n-ary compose of 1 is a problem. Isn't that just identity? And nullary compose is just the the void operator.
Oh, and there's a reason most
compose
functions accept multiple arguments: it would become way too unwieldy with all the parentheses otherwise.
- 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.
I have no idea whether this use case is important. I was mostly just wondering whether there was something problematic about construct.
- 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 samethis
.
- Sets its length to the left operand.
- 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 tofunction () { return g.call(this, f.apply(this, arguments)) }
(mod type checks).
One quibble.
The problem with this is that non-strict functions replace a this
of null with
a reference to the global object, so this would prevent a strict f and
g from distinguishing between
(f >=> g).call(null, x)
and
(f >=> g).call(window, x)
The reason I suggested an operator instead of a function:
- Fewer parentheses is always a plus.
- 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 likely.
Do you a sense of how important this optimization is compared to Array.prototype.{forEach,map}? Why is syntax-enabled optimization a stronger argument for composition than factoring out call overhead in the body of a tight loop?
Why are engines' decisions about when to optimize bad specifically for composition?
I created the email before drafting a more formal strawman (which is almost done - just copy editing left). It's coming soon (it'll be sent directly to the list). I just had last-second issues take up a ton of time with little time to spare (Android malware sucks, especially when no app I've tried can find any traces other than potentially suspicious mic activity, and the fact this is on a stock, non-rooted phone kept fully up to date). Sorry for the delay! :'(
Oh, and most of your concerns will be addressed in some way in the completed strawman.
On Thu, Sep 8, 2016, 14:59 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 1:54 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
Somehow, this missed the list...
---------- Forwarded message --------- From: Isiah Meadows <impinball at gmail.com> Date: Wed, Sep 7, 2016, 12:03 Subject: Re: Function composition syntax To: <mikesamuel at gmail.com>, <es-discuss at mozilla.org>
I was thinking in reverse order, but personally, I'm flexible on specifics like that.
On Wed, Sep 7, 2016, 12:00 Isiah Meadows <isiahmeadows at gmail.com> wrote:
Inline.
On Wed, Sep 7, 2016, 11:05 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 10:10 AM, Isiah Meadows <isiahmeadows at gmail.com
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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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
orFunction.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.
Thanks for answering. Part of the source of my confusion might be that I didn't see a link to any proposal. The only link I saw in the OP was a link to an NPM list of function composition implementations.
See my previous email.
I'm unclear on why an n-ary compose of 1 is a problem. Isn't that just identity? And nullary compose is just the the void operator.
Good point. I did think of those cases, but I'll admit the reasoning is a bit shaky there (and will be largely absent in the strawman).
Oh, and there's a reason most
compose
functions accept multiple arguments: it would become way too unwieldy with all the parentheses otherwise.
- 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.
I have no idea whether this use case is important. I was mostly just wondering whether there was something problematic about construct.
No, there isn't. That's also why I made the change already.
- Create a callable-only function that calls its left operand with the
original arguments and
this
, then calling its right operand with theresult and the same
this
.
- Sets its length to the left operand.
- 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 tofunction () { return g.call(this, f.apply(this, arguments)) }
(mod type checks).One quibble. The problem with this is that non-strict functions replace a
this
of null with a reference to the global object, so this would prevent a strict f and g from distinguishing between(f >=> g).call(null, x)
and
(f >=> g).call(window, x)
The composed function will be a strict function. I was speaking in the case
of strict mode, but loose mode can be fixed by placing a "use strict"
inside the function itself.
The reason I suggested an operator instead of a function:
- Fewer parentheses is always a plus.
- 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 likely.Do you a sense of how important this optimization is compared to Array.prototype.{forEach,map}? Why is syntax-enabled optimization a stronger argument for composition than factoring out call overhead in the body of a tight loop?
Why are engines' decisions about when to optimize bad specifically for composition?
Composition is relatively trivial to generate at runtime, since there's minimal need for even inline cache type checking, and it's a conceptually simple thing even at the imperative level. Syntax would enable engines to optimistically create the pipeline, cache the result if it's recreated, among other things.
One other important case is if you use anonymous functions in the middle:
engines and transpilers won't have to allocate a full function instance for
it, and can instead opt to inline it, leaving just a stack frame for simple
cases like func >=> (x => x + 1)
. That is simply not practical in
practice with a compose
function, even though in theory, the engine
should be able to detect it.
Sharing a common closure across a single sequence is also possible for anonymous functions, because the functions are never used outside of it.
Basically, there are things engines can do with syntax that plainly aren't possible to do with a function, mostly with reducing memory and just doing less to set them up. That would prove useful for applications that use composition very heavily.
Conversely, inlining for Array prototype methods would just benefit speed. Even after inlining, you still have the array and function closure allocated (despite the latter being unused but retained in case of bailout).
Inline (and this missed the list, BTW).
On Thu, Sep 8, 2016, 17:18 Peter Seliger <peter.seliger at googlemail.com>
wrote:
Besides all the strong reasons that do support the argumentation for having a composition operator, one should take into account that there are more than this very special case of
compose
. Function Composition - or maybe Method Modification, as I would prefer referring to it - has already been done in a much wider variety e.g. withbefore
,after
,around
,afterThrowing
andafterFinally
.
I hardly ever see these kinds of functions, and usually code them by hand, anyways, to avoid the excess indirection. So I don't see the need for most of those.
Also, I'm specifically targeting the narrow, common case here. I don't think we need 10 infrequently used wrappers for a function, and they don't belong in the prototype, either.
They all do differ either in theirs arguments signatures and/or in the way of how the result of the first operation gets handled, due to what theirs specific goals are in how they do alter the control flow of a given function via another one.
Function composition is hardly a form of control flow beyond just executing a subroutine.
The special case of currying needs to be mentioned as well, for
curry
does modify methods too.
I intentionally left currying out (as well as partial application) because I wanted to see how this fared as a standalone addition. Arrow functions already are concise enough to be useful.
Introducing a composition operator just for the specific
compose
case, as described by the OP, just would ignore that JavaScript might be rather in need for standardized implementations ofFunction.prototype.before
,Function.prototype.after
,Function.prototype.compose
,Function.prototype.around
,Function.prototype.afterThrowing
,Function.prototype.afterFinally
and evenFunction.prototype.curry
… why not having them all, sincebind
, that also is a modifier, did make it into the standard years ago.
See above. Also, I had very specific, optimization-related reasons due choosing syntax (the partial application proposals have usually used syntax, also).
The additionally provided link to an answer of mine at stackoverflow might help backing up my above opinion.
- "sandwich pattern in javascript code"
stackoverflow.com/questions/11371993/sandwich-pattern-in-javascript-code/27649488#27649488
The sandwich pattern has little to do with composition, if any. I'm not fully convinced you actually understand what function composition is.
My function composition strawman is now live: gist.github.com/isiahmeadows/7b5b49469c08bd3ddc425d15b0bd65c8
Sorry for the wait!
Inline (and this missed the list, BTW).
On Thu, Sep 8, 2016, 17:18 Peter Seliger <peter.seliger at googlemail.com>
wrote:
Besides all the strong reasons that do support the argumentation for having a composition operator, one should take into account that there are more than this very special case of
compose
. Function Composition - or maybe Method Modification, as I would prefer referring to it - has already been done in a much wider variety e.g. withbefore
,after
,around
,afterThrowing
andafterFinally
.
I hardly ever see these kinds of functions, and usually code them by hand, anyways, to avoid the excess indirection. So I don't see the need for most of those.
Also, I'm specifically targeting the narrow, common case here. I don't think we need 10 infrequently used wrappers for a function, and they don't belong in the prototype, either.
They all do differ either in theirs arguments signatures and/or in the way of how the result of the first operation gets handled, due to what theirs specific goals are in how they do alter the control flow of a given function via another one.
Function composition is hardly a form of control flow beyond just executing a subroutine.
The special case of currying needs to be mentioned as well, for
curry
does modify methods too.
I intentionally left currying out (as well as partial application) because I wanted to see how this fared as a standalone addition. Arrow functions already are concise enough to be useful.
Introducing a composition operator just for the specific
compose
case, as described by the OP, just would ignore that JavaScript might be rather in need for standardized implementations ofFunction.prototype.before
,Function.prototype.after
,Function.prototype.compose
,Function.prototype.around
,Function.prototype.afterThrowing
,Function.prototype.afterFinally
and evenFunction.prototype.curry
… why not having them all, sincebind
, that also is a modifier, did make it into the standard years ago.
See above. Also, I had very specific, optimization-related reasons due choosing syntax (the partial application proposals have usually used syntax, also).
The additionally provided link to an answer of mine at stackoverflow might help backing up my above opinion.
- "sandwich pattern in javascript code"
stackoverflow.com/questions/11371993/sandwich-pattern-in-javascript-code/27649488#27649488
The sandwich pattern has little to do with composition, if any. I'm not fully convinced you actually understand what function composition is.
Somehow, this missed the list...
---------- Forwarded message --------- From: Isiah Meadows <impinball at gmail.com>
Date: Wed, Sep 7, 2016, 12:03 Subject: Re: Function composition syntax To: <mikesamuel at gmail.com>, <es-discuss at mozilla.org>
I was thinking in reverse order, but personally, I'm flexible on specifics like that.
Inline.
On Wed, Sep 7, 2016, 11:05 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 10:10 AM, Isiah Meadows <isiahmeadows at gmail.com> 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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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
orFunction.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
otherwise.
- 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.
- 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 samethis
.
- Sets its length to the left operand.
- 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
g.call(this, f.apply(this, arguments)) }` (mod type checks).
The reason I suggested an operator instead of a function:
- Fewer parentheses is always a plus.
- 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
likely.
I created the email before drafting a more formal strawman (which is almost done - just copy editing left). It's coming soon (it'll be sent directly to the list). I just had last-second issues take up a ton of time with little time to spare (Android malware sucks, especially when no app I've tried can find any traces other than potentially suspicious mic activity, and the fact this is on a stock, non-rooted phone kept fully up to date). Sorry for the delay! :'(
interesting proposal!
Here's what I propose: a new infix operator
>=>
(operator and direction can change) for composing two functions.
Sweet, reminds me of Kleisli composition in Haskell hackage.haskell.org/package/base-4.9.0.0/docs/Control-Monad.html#v:-62--61--62- (which does something different to functions though).
- It allows engines to statically optimize functions in the middle (avoid an extra function allocation), like with
f >=> x => console.log("x:" + x)
.
I don't understand that one. Wouldn't x => console.log("x:" + f(x))
be
optimised better (and also be easier to read)?
- It can simplify the internal model some to deal with a binary pair instead of an array, especially when pipelining gets involved.
- Composition isn't usually combined as a function in JS.
Can you clarify what you mean with this?
My questions would be
- What precedence would the operator have? Clearly something between member access and assignment, but what exactly? Particularly interesting cases: f >=> g (x) f >=> p ? g : h f >=> x => x >=> g
- Do we also need a partial application operator to make this syntax useful? I guess the discussions from tc39/proposal-bind-operator#35 and tc39/proposal-bind-operator#26 are relevant here.
Kind , Bergi
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 samethis
.
IMHO, the order seems wrong. Not sure if programming languages do it differently, but in math composition works like this:
(f ? g)(x) = f(g(x))
So I think it would be more intuitive to use <=<
, which would call the right operand first, and then call the left operand with the result of the right one.
,Oriol
Inline.
On Wed, Sep 7, 2016, 10:44 Bergi <a.d.bergi at web.de> wrote:
Hi, interesting proposal!
Here's what I propose: a new infix operator
>=>
(operator and direction can change) for composing two functions.Sweet, reminds me of Kleisli composition in Haskell
hackage.haskell.org/package/base-4.9.0.0/docs/Control-Monad.html#v:-62--61--62- (which does something different to functions though).
That was an inspiration, but that adds to the list of reasons why I would be okay with a different operator.
- It allows engines to statically optimize functions in the middle (avoid an extra function allocation), like with
f >=> x => console.log("x:" + x)
.I don't understand that one. Wouldn't
x => console.log("x:" + f(x))
be optimised better (and also be easier to read)?
Yes, but it was a trivial example just for the sake of example.
- It can simplify the internal model some to deal with a binary pair instead of an array, especially when pipelining gets involved.
- Composition isn't usually combined as a function in JS.
Can you clarify what you mean with this?
Things like compose(compose, compose)
aren't frequently used in JS
(unlike Haskell's (.) . (.)
). I almost included that example, but I
couldn't get a wording I liked.
My questions would be
- What precedence would the operator have? Clearly something between member access and assignment, but what exactly? Particularly interesting cases: f >=> g (x) f >=> p ? g : h f >=> x => x >=> g
I intentionally left precedence out (other than implying it's chainable left to right), to avoid complicating the initial presentation.
- Do we also need a partial application operator to make this syntax useful?
I guess the discussions from tc39/proposal-bind-operator#35 and tc39/proposal-bind-operator#26 are relevant here.
It would help, but no, that's not required. Arrow functions can help in the meantime, and may be more readable, and sometimes required, in some cases, since it's more explicit.
(It's come up before, independently: esdiscuss.org/topic/syntax-sugar-for-partial-application)
On Wed, Sep 7, 2016 at 1:54 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
Somehow, this missed the list...
---------- Forwarded message --------- From: Isiah Meadows <impinball at gmail.com> Date: Wed, Sep 7, 2016, 12:03 Subject: Re: Function composition syntax To: <mikesamuel at gmail.com>, <es-discuss at mozilla.org>
I was thinking in reverse order, but personally, I'm flexible on specifics like that.
On Wed, Sep 7, 2016, 12:00 Isiah Meadows <isiahmeadows at gmail.com> wrote:
Inline.
On Wed, Sep 7, 2016, 11:05 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 10:10 AM, Isiah Meadows <isiahmeadows at gmail.com> 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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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
orFunction.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.
Thanks for answering. Part of the source of my confusion might be that I didn't see a link to any proposal. The only link I saw in the OP was a link to an NPM list of function composition implementations.
I'm unclear on why an n-ary compose of 1 is a problem. Isn't that just identity? And nullary compose is just the the void operator.
Oh, and there's a reason most
compose
functions accept multiple arguments: it would become way too unwieldy with all the parentheses otherwise.
- 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.
I have no idea whether this use case is important. I was mostly just wondering whether there was something problematic about construct.
- 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 samethis
.
- Sets its length to the left operand.
- 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 tofunction () { return g.call(this, f.apply(this, arguments)) }
(mod type checks).
One quibble.
The problem with this is that non-strict functions replace a this
of null with
a reference to the global object, so this would prevent a strict f and
g from distinguishing between
(f >=> g).call(null, x)
and
(f >=> g).call(window, x)
The reason I suggested an operator instead of a function:
- Fewer parentheses is always a plus.
- 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 likely.
Do you a sense of how important this optimization is compared to Array.prototype.{forEach,map}? Why is syntax-enabled optimization a stronger argument for composition than factoring out call overhead in the body of a tight loop?
Why are engines' decisions about when to optimize bad specifically for composition?
Oh, and most of your concerns will be addressed in some way in the completed strawman.
On Thu, Sep 8, 2016, 14:59 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 1:54 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
Somehow, this missed the list...
---------- Forwarded message --------- From: Isiah Meadows <impinball at gmail.com> Date: Wed, Sep 7, 2016, 12:03 Subject: Re: Function composition syntax To: <mikesamuel at gmail.com>, <es-discuss at mozilla.org>
I was thinking in reverse order, but personally, I'm flexible on specifics like that.
On Wed, Sep 7, 2016, 12:00 Isiah Meadows <isiahmeadows at gmail.com> wrote:
Inline.
On Wed, Sep 7, 2016, 11:05 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 10:10 AM, Isiah Meadows <isiahmeadows at gmail.com
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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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
orFunction.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.
Thanks for answering. Part of the source of my confusion might be that I didn't see a link to any proposal. The only link I saw in the OP was a link to an NPM list of function composition implementations.
See my previous email.
I'm unclear on why an n-ary compose of 1 is a problem. Isn't that just identity? And nullary compose is just the the void operator.
Good point. I did think of those cases, but I'll admit the reasoning is a bit shaky there (and will be largely absent in the strawman).
Oh, and there's a reason most
compose
functions accept multiple arguments: it would become way too unwieldy with all the parentheses otherwise.
- 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.
I have no idea whether this use case is important. I was mostly just wondering whether there was something problematic about construct.
No, there isn't. That's also why I made the change already.
- Create a callable-only function that calls its left operand with the
original arguments and
this
, then calling its right operand with theresult and the same
this
.
- Sets its length to the left operand.
- 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 tofunction () { return g.call(this, f.apply(this, arguments)) }
(mod type checks).One quibble. The problem with this is that non-strict functions replace a
this
of null with a reference to the global object, so this would prevent a strict f and g from distinguishing between(f >=> g).call(null, x)
and
(f >=> g).call(window, x)
The composed function will be a strict function. I was speaking in the case
of strict mode, but loose mode can be fixed by placing a "use strict"
inside the function itself.
The reason I suggested an operator instead of a function:
- Fewer parentheses is always a plus.
- 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 likely.Do you a sense of how important this optimization is compared to Array.prototype.{forEach,map}? Why is syntax-enabled optimization a stronger argument for composition than factoring out call overhead in the body of a tight loop?
Why are engines' decisions about when to optimize bad specifically for composition?
Composition is relatively trivial to generate at runtime, since there's minimal need for even inline cache type checking, and it's a conceptually simple thing even at the imperative level. Syntax would enable engines to optimistically create the pipeline, cache the result if it's recreated, among other things.
One other important case is if you use anonymous functions in the middle:
engines and transpilers won't have to allocate a full function instance for
it, and can instead opt to inline it, leaving just a stack frame for simple
cases like func >=> (x => x + 1)
. That is simply not practical in
practice with a compose
function, even though in theory, the engine
should be able to detect it.
Sharing a common closure across a single sequence is also possible for anonymous functions, because the functions are never used outside of it.
Basically, there are things engines can do with syntax that plainly aren't possible to do with a function, mostly with reducing memory and just doing less to set them up. That would prove useful for applications that use composition very heavily.
Conversely, inlining for Array prototype methods would just benefit speed. Even after inlining, you still have the array and function closure allocated (despite the latter being unused but retained in case of bailout).
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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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:
- Verify both operands are callable.
- 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 samethis
. - Sets its length to the left operand.
- Return the new function.
The reason I suggested an operator instead of a function:
-
Fewer parentheses is always a plus.
-
It allows engines to statically optimize functions in the middle (avoid an extra function allocation), like with
f >=> x => console.log("x:" + x)
. -
It can simplify the internal model some to deal with a binary pair instead of an array, especially when pipelining gets involved.
-
Composition isn't usually combined as a function in JS.
What do you all think?
My function composition strawman is now live: gist.github.com/isiahmeadows/7b5b49469c08bd3ddc425d15b0bd65c8
Sorry for the wait!
Oh, and most of your concerns will be addressed in some way in the completed strawman.
On Thu, Sep 8, 2016, 14:59 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 1:54 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
Somehow, this missed the list...
---------- Forwarded message --------- From: Isiah Meadows <impinball at gmail.com> Date: Wed, Sep 7, 2016, 12:03 Subject: Re: Function composition syntax To: <mikesamuel at gmail.com>, <es-discuss at mozilla.org>
I was thinking in reverse order, but personally, I'm flexible on specifics like that.
On Wed, Sep 7, 2016, 12:00 Isiah Meadows <isiahmeadows at gmail.com> wrote:
Inline.
On Wed, Sep 7, 2016, 11:05 Mike Samuel <mikesamuel at gmail.com> wrote:
On Wed, Sep 7, 2016 at 10:10 AM, Isiah Meadows <isiahmeadows at gmail.com
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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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
orFunction.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.
Thanks for answering. Part of the source of my confusion might be that I didn't see a link to any proposal. The only link I saw in the OP was a link to an NPM list of function composition implementations.
See my previous email.
I'm unclear on why an n-ary compose of 1 is a problem. Isn't that just identity? And nullary compose is just the the void operator.
Good point. I did think of those cases, but I'll admit the reasoning is a bit shaky there (and will be largely absent in the strawman).
Oh, and there's a reason most
compose
functions accept multiple arguments: it would become way too unwieldy with all the parentheses otherwise.
- 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.
I have no idea whether this use case is important. I was mostly just wondering whether there was something problematic about construct.
No, there isn't. That's also why I made the change already.
- Create a callable-only function that calls its left operand with the
original arguments and
this
, then calling its right operand with theresult and the same
this
.
- Sets its length to the left operand.
- 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 tofunction () { return g.call(this, f.apply(this, arguments)) }
(mod type checks).One quibble. The problem with this is that non-strict functions replace a
this
of null with a reference to the global object, so this would prevent a strict f and g from distinguishing between(f >=> g).call(null, x)
and
(f >=> g).call(window, x)
The composed function will be a strict function. I was speaking in the case
of strict mode, but loose mode can be fixed by placing a "use strict"
inside the function itself.
The reason I suggested an operator instead of a function:
- Fewer parentheses is always a plus.
- 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 likely.Do you a sense of how important this optimization is compared to Array.prototype.{forEach,map}? Why is syntax-enabled optimization a stronger argument for composition than factoring out call overhead in the body of a tight loop?
Why are engines' decisions about when to optimize bad specifically for composition?
Composition is relatively trivial to generate at runtime, since there's minimal need for even inline cache type checking, and it's a conceptually simple thing even at the imperative level. Syntax would enable engines to optimistically create the pipeline, cache the result if it's recreated, among other things.
One other important case is if you use anonymous functions in the middle:
engines and transpilers won't have to allocate a full function instance for
it, and can instead opt to inline it, leaving just a stack frame for simple
cases like func >=> (x => x + 1)
. That is simply not practical in
practice with a compose
function, even though in theory, the engine
should be able to detect it.
Sharing a common closure across a single sequence is also possible for anonymous functions, because the functions are never used outside of it.
Basically, there are things engines can do with syntax that plainly aren't possible to do with a function, mostly with reducing memory and just doing less to set them up. That would prove useful for applications that use composition very heavily.
Conversely, inlining for Array prototype methods would just benefit speed. Even after inlining, you still have the array and function closure allocated (despite the latter being unused but retained in case of bailout).
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: www.npmjs.com/search?q=function+composition
- Numerous manual implementations
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
- They can create pipelines to optimize multiple composition chains together.
- They can avoid most of the closure allocation cost internally.
- 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:
- Verify both operands are callable.
- 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 samethis
. - Sets its length to the left operand.
- Return the new function.
The reason I suggested an operator instead of a function:
-
Fewer parentheses is always a plus.
-
It allows engines to statically optimize functions in the middle (avoid an extra function allocation), like with
f >=> x => console.log("x:" + x)
. -
It can simplify the internal model some to deal with a binary pair instead of an array, especially when pipelining gets involved.
-
Composition isn't usually combined as a function in JS.
What do you all think?
My function composition strawman is now live: gist.github.com/isiahmeadows/7b5b49469c08bd3ddc425d15b0bd65c8
Sorry for the wait!
I use a pipe
function all the time and would love to see this proposal or
a Function.pipe
make it into the spec.
See isiahmeadows/function-composition-proposal for an update to the proposal.
I would like to see a function composition operator make it into the language. Currently, there is:
_.flow
and_.flowRight
(lodash/fp alias:_.compose
and_.composeRight
)_.compose
R.compose
Function composition could be far more efficiently implemented in the engine, in ways not possible at the language level:
Here's what I propose: a new infix operator
>=>
(operator and directioncan change) for composing two functions. It would do the following:
this
, then calling its right operand with the result and the samethis
.The reason I suggested an operator instead of a function:
Fewer parentheses is always a plus.
It allows engines to statically optimize functions in the middle (avoid an extra function allocation), like with
f >=> x => console.log("x:" + x)
.It can simplify the internal model some to deal with a binary pair instead of an array, especially when pipelining gets involved.
Composition isn't usually combined as a function in JS.
What do you all think?