Function composition syntax

Isiah Meadows isiahmeadows at gmail.com
Thu Sep 29 00:05:53 UTC 2016


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! :'(

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:
> >>> > https://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:
> >>> >
> >>> > 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.
>
> 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.
>
>
>
> >>> > 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.
>
> I have no idea whether this use case is important.
> I was mostly just wondering whether there was something problematic
> about construct.
>
>
>
> >>> > 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
> >> 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:
> >>> >
> >>> > 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
> >> 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?
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160929/afc4df0e/attachment.html>


More information about the es-discuss mailing list