Named Arrow Functions

# Jacob Parker (9 years ago)

I did look, but couldn’t find anything on named arrow functions were not included. I do sometimes find cases where I want recursion inside a class function definition, and still need access to this. Was it just seen as syntax bloat, or were there any complications to implementing it?

Obviously a contrived example, but something like this (using do syntax too)

x.map(factorial(x) => do {
    if (x <= 1) {
        1;
    } else {
        x * factorial(x - 1)
    }
});
# Tab Atkins Jr. (9 years ago)

On Tue, Aug 11, 2015 at 1:49 PM, Jacob Parker <jacobparker1992 at gmail.com> wrote:

I did look, but couldn’t find anything on named arrow functions were not included. I do sometimes find cases where I want recursion inside a class function definition, and still need access to this. Was it just seen as syntax bloat, or were there any complications to implementing it?

Obviously a contrived example, but something like this (using do syntax too)

x.map(factorial(x) => do {
    if (x <= 1) {
        1;
    } else {
        x * factorial(x - 1)
    }
});

Probably not super-helpful in practice, but there's always the Y-combinator. ^_^

let Y = F => (x=>F(y=>(x(x))(y)))(x=>F(y=>(x(x))(y)));

x.map(Y(fact=>x=> do { if( x <= 1) { 1; } else { x * fact(x-1); } ));

Here, the syntax "Y(foo=>" indicates a "named" arrow function.

# James M Snell (9 years ago)

On Tue, Aug 11, 2015 at 2:21 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote: [snip]

let Y = F => (x=>F(y=>(x(x))(y)))(x=>F(y=>(x(x))(y)));

Aahhhhhh! my eyes! it burns!!!

# Leonardo Wolter (9 years ago)

Woah, an actual application of Y combinator hahaha

www.youtube.com/watch?v=FITJMJjASUs

# Daniel Ehrenberg (9 years ago)

In addition to being hard to parse in general, I don't think this would play very well with the async/await proposal tc39.github.io/ecmascript-asyncawait , which wants to have arrow functions like

async (x) => ...

Because we can't count on async as a keyword, your proposal would create an ambiguity.

# Leonardo Wolter (9 years ago)

What about a clojure like recur hidden variable binded to the bottom-level function?

x.map(factorial(x) => do { if (x <= 1) { 1; } else { x * recur(x - 1) } });

2015-08-11 21:26 GMT-03:00 Daniel Ehrenberg <dehrenberg at chromium.org>:

# Daniel Ehrenberg (9 years ago)

I assume you mean more like this (without factorial):

x.map((x) => do { if (x <= 1) { 1; } else { x * recur(x - 1) } });

One issue is that it's hard to add keywords to JavaScript at this point. If they're not from the tiny set of remaining reserved words (enum, anyone?), they can be users' identifiers, and have to be based contextually on some enclosing syntax, like yield is.

Another downside is that then, arrow functions have a distinct and less powerful method of recursing (e.g., nested functions won't be able to see the binding to the outer one).

Dan

# Leonardo Wolter (9 years ago)

Yeah., that's what I meant.

My proposal is not a keyword, but an hidden variable included at functions (e.g. arguments). Does arrow functions have any limitations about that?

2015-08-11 21:35 GMT-03:00 Daniel Ehrenberg <dehrenberg at chromium.org>:

# Leonardo Wolter (9 years ago)

Well, I found out arguments is actually a reserved word too haha

About that:

If they're not from the tiny set of remaining reserved words (enum, anyone?), they can be users' identifiers, and have to be based contextually on some enclosing syntax, like yield is.

That could be it, right? Since it would be only available at arrow functions(anon functions too?)

2015-08-11 21:42 GMT-03:00 Leonardo Wolter <leocwolter at gmail.com>:

# Kevin Smith (9 years ago)
x.map(factorial(x) => do {
    if (x <= 1) {
        1;
    } else {
        x * factorial(x - 1)
    }
});

This has been discussed over the years but there has been very little interest in making the grammar more complicated. After all, you can always just assign the arrow function to a locally-scoped variable. And as Daniel pointed out, this suggestion actually conflicts with async arrow functions.

# Kevin Smith (9 years ago)

My proposal is not a keyword, but an hidden variable included at functions (e.g. arguments). Does arrow functions have any limitations about that?

Yes. There are no special contextual keywords reserved within arrow functions. We've discussed the possibility of a "meta property" for this (e.g. "function.self") but there's little interest at present.

# Isiah Meadows (9 years ago)

The real reason people need named arrow functions, the biggest use case is for event handlers.

let p = new Promise((resolve, reject) =>
  setTimeout((x => () => x(x))(handler => {
    onNotNeeded(() => clearTimeout(handler));

    // `return` is to take advantage of TCO
    return doSomethingAsync(err => {
      if (err != null) return reject(err)
      else return resolve();
    });
  }));

By the way, the way I created a self reference is a complete lambda calculus hack.

<deviation>

If you'd like your eyes to bleed, here's one that is purely out if lambdas. I couldn't help myself.

let p = new Promise((resolve, reject) =>
  setTimeout((x => () => x(x))(h =>
    x => y => y(x()))(
      onNotNeeded(() => clearTimeout(h)))(
      doSomethingAsync(e =>

      if (err != null) return reject(err)
      else return resolve();
    });
  }));
# Isiah Meadows (9 years ago)

Sent this too early... Corrected inline.

On Tue, Aug 11, 2015, 21:56 Isiah Meadows <isiahmeadows at gmail.com> wrote:

The real reason people need named arrow functions, the biggest use case is for event handlers.

let p = new Promise((resolve, reject) =>
  setTimeout((x => () => x(x))(handler => {
    onNotNeeded(() => clearTimeout(handler));

    // `return` is to take advantage of TCO
    return doSomethingAsync(err => {
      if (err != null) return reject(err)
      else return resolve();
    });
  }));

By the way, the way I created a self reference is a complete lambda calculus hack.

<deviation>

If you'd like your eyes to bleed, here's one that is purely out if lambdas. I couldn't help myself.

let p = new Promise((resolve, reject) =>
  setTimeout((x => () => x(x))(h =>
    x => y => y(x()))(
      onNotNeeded(() => clearTimeout(h)))(
      doSomethingAsync(e =>


e

!= null

?

reject(err)

:

  resolve()

)))

);

On Tue, Aug 11, 2015, 20:45 Leonardo Wolter <leocwolter at gmail.com> wrote:

Well, I found out arguments is actually a reserved word too haha

About that:

If they're not from the tiny set of remaining reserved words (enum, anyone?), they can be users' identifiers, and have to be based contextually on some enclosing syntax, like yield is.

That could be it, right? Since it would be only available at arrow functions(anon functions too?)

2015-08-11 21:42 GMT-03:00 Leonardo Wolter <leocwolter at gmail.com>:

Yeah., that's what I meant.

My proposal is not a keyword, but an hidden variable included at functions (e.g. arguments).

Does arrow functions have any limitations about that?

2015-08-11 21:35 GMT-03:00 Daniel Ehrenberg <dehrenberg at chromium.org>:

I assume you mean more like this (without factorial):

x.map((x) => do { if (x <= 1) { 1; } else { x * recur(x - 1) } });

One issue is that it's hard to add keywords to JavaScript at this point. If they're not from the tiny set of remaining reserved words (enum, anyone?), they can be users' identifiers, and have to be based contextually on some enclosing syntax, like yield is.

Another downside is that then, arrow functions have a distinct and less powerful method of recursing (e.g., nested functions won't be able to see the binding to the outer one).

Dan

On Tue, Aug 11, 2015 at 5:30 PM, Leonardo Wolter <leocwolter at gmail.com>

wrote:

What about a clojure like recur hidden variable binded to the bottom-level function?

x.map(factorial(x) => do { if (x <= 1) { 1; } else { x * recur(x - 1) } });

2015-08-11 21:26 GMT-03:00 Daniel Ehrenberg <dehrenberg at chromium.org>:

In addition to being hard to parse in general, I don't think this would play very well with the async/await proposal tc39.github.io/ecmascript-asyncawait , which wants to have arrow functions like

async (x) => ...

Because we can't count on async as a keyword, your proposal would create an ambiguity.

On Tue, Aug 11, 2015 at 1:49 PM, Jacob Parker <jacobparker1992 at gmail.com> wrote:

I did look, but couldn’t find anything on named arrow functions were

not

included. I do sometimes find cases where I want recursion inside a

class

function definition, and still need access to this. Was it just seen

as

# Isiah Meadows (9 years ago)

And as Kevin said, it has been mentioned before (with event handlers and timeouts as the initial driving force).

esdiscuss.org/topic/self

# Salvador de la Puente González (9 years ago)

Ups, I answered only to Jacob but I think this is a real simple solution:

var factorial;
x.map(factorial = x =>  x < 1 ? 1 : x * factorial(x-1));

Or simply:

var factorial = x =>  x < 1 ? 1 : x * factorial(x-1);
x.map(factorial);

As you said, this is ocassionally needed. And for event handlers:

var once = evt => { button.onclick = null; onClick(evt) };
button.onclick = once;

And yes, this solution was already provided: esdiscuss.org/topic/self-recursion-and-arrow-functions#content

# Ron Waldon (9 years ago)

For use cases that require named Functions (e.g. recursion), surely it's not a such a big deal to either assign an Array Function to a variable first, or use the good old trusty named Function expression or Function statement.

var recurseExample = () => { recurseExample(); }

var recurseExample = function recurseExample () { recurseExample(); }

function recurseExample () { recurseExample(); }

I wonder if it's even a good idea to use Arrow Functions in places that do not benefit from the new this behaviour? Just because code takes up fewer characters does not necessarily mean it is easier to understand.

# Nick Krempel (9 years ago)

On 12 August 2015 at 02:56, Isiah Meadows <isiahmeadows at gmail.com> wrote:


let p = new Promise((resolve, reject) =>
  setTimeout((x => () => x(x))(handler => {
    onNotNeeded(() => clearTimeout(handler));

    // `return` is to take advantage of TCO
    return doSomethingAsync(err => {
      if (err != null) return reject(err)
      else return resolve();
    });
  }));

This doesn't work as the function passed to clearTimeout does not === the function passed to setTimeout.

In fact, they're not even behaviorally equal as the function passed to setTimeout is expecting no parameters and the function passed to clearTimeout is expecting one parameter - i.e. this is not even correct in lambda calculus.

A lambda-calculus-correct version could be:

setTimeout((x=>(y=>()=>x(y(y)))(y=>()=>x(y(y))))(handler => {...}));

But this would still suffer from the object identity problem mentioned above. A final JS-correct version could be:

setTimeout((x => {const y = () => x(y); return y;})(handler => {...}));

Nick

# Salvador de la Puente González (9 years ago)

AFAIK, this wont work because what clearTimeout() is actually expecting is the id returned by setTimeout(). El 12/8/2015 16:00, "Nick Krempel" <ndkrempel at google.com> escribió:

# Nick Krempel (9 years ago)

This was assuming an API like addEventListener/removeEventListener, not the standard DOM setTimeout/clearTimeout, sorry for the confusion.

# Isiah Meadows (9 years ago)

Oops... Used to Node world. I didn't really test it, since I replied by phone.