recursive arrow functions

# Irakli Gozalishvili (7 years ago)

Today I have noticed one interesting remark from Douglas Crockford on arrow functions: "you will need to use the old functions to write self-recursive functions" - www.yuiblog.com/blog/2012/03/30/what-is-the-meaning-of-this

Is that really going to be the case ? I was so much looking forward to proper tail calls and making arrow functions incompatible would be really bad, specially since they seem to be more functiony than current functions. Why not allow naming arrow function or have some syntax for making recursive calls ?

-- Irakli Gozalishvili Web: www.jeditoolkit.com

# Brendan Eich (7 years ago)

See esdiscuss/2012-March/021903 -- easy to do provided these are just named arrow function expressions, not declarations. I'll work on consensus.

# Kevin Smith (7 years ago)

This is the workaround, right?

const recurse = () => {
    // call recurse(), somehow...
};

If so, then it seems hardly worth any added parsing complexity to support named arrows.

# Claus Reinke (7 years ago)

Today I have noticed one interesting remark from Douglas Crockford on arrow functions: "you will need to use the old functions to write self-recursive functions" - www.yuiblog.com/blog/2012/03/30/what-is-the-meaning-of-this

Is that really going to be the case ? I was so much looking forward to proper tail calls and making arrow functions incompatible would be really bad, specially since they seem to be more functiony than current functions. Why not allow naming arrow function or have some syntax for making recursive calls ?

That post needs to be updated to the current spec (this-handling). Since it is already attracting attention, it is too late to retract it.

Let-bindings already allow for some forms of recursion. To get recursion at expression level, you can always define your own recursion combinator. Something like

// ref(f) produces f(rec(f) modulo some eta-expansions
let rec = (f)=> (..args)=> f( (..args)=>rec(f)(..args), ..args )

Applying rec to an arrow function would supply the latter with a self-reference and

rec( (f,n) => (n>1 ? n*f(n-1) : n) )(6)

would then return 720. This would work even without consensus on named arrow functions.

I would suggest that utilities like 'rec' and my earlier 'getthis' (renamed to 'fn') should go into a standard library. To get a feel for what other utilities make sense in ES6, partial implementations of ES6 need to start supporting the (brand-new) arrow consensus.

Claus

# Brendan Eich (7 years ago)

Kevin Smith wrote:

This is the workaround, right?

const recurse = () => {
    // call recurse(), somehow...
};

Yes, with the hassle of hoisting from an expression out to enclosing block or body scope, and of polluting that scope with the name (long-form named function expressions avoid this).

If so, then it seems hardly worth any added parsing complexity to support named arrows.

Could be.

# Allen Wirfs-Brock (7 years ago)

On Apr 1, 2012, at 7:59 AM, Brendan Eich wrote:

Kevin Smith wrote:

This is the workaround, right?

const recurse = () => { // call recurse(), somehow... };

Yes, with the hassle of hoisting from an expression out to enclosing block or body scope, and of polluting that scope with the name (long-form named function expressions avoid this).

If so, then it seems hardly worth any added parsing complexity to support named arrows. Could be.

or: (do {let recurse = () => { //call recurse(), somehow ... }; recurse } )

Plus, if we can define const and let to have completion values, we can get rid of the extra reference to recurse.

# Brendan Eich (7 years ago)

While

harmony:completion_reform

hasn't settled the matter, I think it would be "bad" for let and const to differ from var in

y = 33; for (var x = 42; x < 0;);

which completes with value undefined according to the completion-reform proposal (but completes with 33 in ES1-5; we are using one finger of fate to tempt fate with an incompatible change here).

If I change var to let, why should the completion change (under completion-reform) from undefined to 42?

# Claus Reinke (7 years ago)

To get recursion at expression level, you can always define your own recursion combinator. Something like

// ref(f) produces f(rec(f) modulo some eta-expansions let rec = (f)=> (..args)=> f( (..args)=>rec(f)(..args), ..args )

Applying rec to an arrow function would supply the latter with a self-reference and

rec( (f,n) => (n>1 ? n*f(n-1) : n) )(6)

would then return 720. This would work even without consensus on named arrow functions.

Here is my attempt to translate this to ES5

function rec(f) { return function() { return f.apply(this,[function(){return rec(f).apply(this,arguments)}] .concat(Array.prototype.slice.call(arguments)) ) } }

console.log( rec( function(f,n) { return n>1 ? n*f(n-1) : n } )(6) );

Outputs 720, and shows that all those little syntax simplifications add up: while the idea is nearly readable from the ES6 version of the code, the ES5 version obscures it so much that I've got to think about the encoding as much as about the recursion combinator itself; also, I'd rate my chances of finding bugs lower in the ES5 version (apart from not having an ES6 implementation).

Claus