366

That is, how do I express

function *(next) {}

with arrow syntax? I've tried all the combinations I could think of, and I can't find any documentation on it.

(I am currently using Node.js v0.11.14.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ashley Coolman
  • 11,095
  • 5
  • 59
  • 81
  • 6
    You can't. Sorry. "The `function*` statement (function keyword followed by an asterisk) defines a generator function." –  Dec 26 '14 at 19:55
  • 1
    https://github.com/rwaldron/tc39-notes/blob/c61f48cea5f2339a1ec65ca89827c8cff170779b/es6/2013-11/nov-20.md#410-generator-arrow-function-syntax – vkurchatkin Dec 26 '14 at 20:03
  • 2
    Note that there was a somewhat-lengthy discussion on this topic [at esdiscuss.org](https://esdiscuss.org/topic/generator-arrow-functions). – voithos Dec 26 '14 at 20:05
  • What is the purpose of the parentheses around `next`? I think this is invalid syntax. –  Apr 20 '15 at 08:47
  • `next` is just an argument being passed into the function, isn't it? Admitedly it isn't needed for the example ... – Jed Richards Apr 27 '15 at 20:16
  • 4
    What do you expect `param*=>{ }` to do? – CoderPi Dec 05 '15 at 16:37
  • @CodeiSir to provide an in-line generator function. – vitaly-t Dec 05 '15 at 16:37
  • 4
    you know that `function(){}` is not doing the same as `()=>{}` ? – CoderPi Dec 05 '15 at 16:40
  • 1
    @CodeiSir, how is this relevant? – vitaly-t Dec 05 '15 at 16:41
  • 11
    "*is it really that ES6 generators are 2 steps forward and 1 step back?*" - no, [generators can only step forward](http://stackoverflow.com/a/23848531/1048572) :-) – Bergi Dec 05 '15 at 17:56
  • @vitaly-t: It's relevant as far as [arrow functions are not just a syntactic replacement for bound function expressions](http://stackoverflow.com/q/32535110/1048572), and that you cannot expect the same syntax to work for other functions. – Bergi Dec 05 '15 at 17:58
  • @Bergi the link to the "doublicate" does not contain an explanation. Should I move my answer to the other post? – CoderPi Dec 05 '15 at 18:00
  • @Bergi I think it does explain it: "As a consequence, arrow functions cannot be used as generators." – CoderPi Dec 05 '15 at 18:57
  • @CodeiSir: But why is that so? The spec could pretty trivially have allowed us to use a syntax like `(param) =*> { yield param; }` (or whatever). Your answer doesn't give the reason why that was not done. – Bergi Dec 05 '15 at 19:00
  • well I then state "Why Arrow-function can not use yield" wich together explains why it can not be done – CoderPi Dec 05 '15 at 19:14
  • 1
    *()={} would be the consistent path for what yo are asking in ES6... but I think it is not available yet... – ZEE Oct 28 '16 at 16:01
  • 1
    I really don't understand the generator designator asterisk. Why not make any function containing a yield statement a generator function like Python? – Abram Dec 26 '16 at 10:44
  • 3
    I really wish they would have just used 'generator' as a keyword in place of function. The asterisk feels so random. – Costa Michailidis Jul 05 '17 at 16:43

8 Answers8

346

Can I use ES6's arrow function syntax with generators?

You can't. Sorry.

According to MDN

The function* statement (function keyword followed by an asterisk) defines a generator function.

From a spec document (my emphasis):

The function syntax is extended to add an optional * token:

FunctionDeclaration: "function" "*"? Identifier "(" FormalParameterList? ")" 
  "{" FunctionBody "}"
tagurit
  • 494
  • 5
  • 13
  • 277
    Feels like a design flaw to me. – Jonathon Apr 17 '15 at 09:05
  • 33
    @Jonathon: No. Arrow functions are supposed to be light-weight (and don't have a `.prototype` for example) and often one-liners, while generators are pretty much the opposite. – Bergi Apr 17 '15 at 13:26
  • 62
    I have already run across a few scenarios where a generator I was playing with needed access to the previous `this`, and had to write the `let self = this` hack to get access to it inside the generator. The lexical scope + arrow syntax would have been nice. Unfortunate, but not exactly the end of the world. – dvlsg Apr 17 '15 at 23:44
  • 2
    @Bergi: Do you have any idea of whether they're handled differently under the surface? Things may have changed but last time I checked arrow functions were just normal functions automatically wrapped in `bind()`. – Jonathon Apr 18 '15 at 07:20
  • 2
    @Jonathon: I don't think they ever were. They might be transpiled to `.bind`, but that's not how they are specced in ES6. – Bergi Apr 18 '15 at 11:14
  • 3
    Some additional context around this at [esdiscuss](https://esdiscuss.org/topic/generator-arrow-functions) – Nick Tomlin Nov 12 '15 at 20:29
  • 59
    @Bergi the reasoning behind arrow functions is a lot more complicated than that. It's not really about brevity. Arrow functions needn't be lightweight – it's true there's an optional single-statement body syntax, but so what. Many people use arrows for all function definitions except class methods, and demote the `function` keyword to being a 'bad part' of the language. There are good reasons to do this. For these people, the lack of arrow generators is an annoying inconsistency. – callum Apr 07 '17 at 17:15
  • 6
    @callum I did mean lightweight in terms of instance creation and call overhead, not syntax. Not sure what you think the reasoning behind them was. And no, I don't see any good reasons to use non-declarative arrow function expressions over `function` declarations. – Bergi Apr 08 '17 at 07:31
  • 1
    @callum Agreed. I always try to stick with `class` for creating objects and arrow functions as first-class lambdas (e.g. argument to a method), in addition to methods (instance and static) as needed. Sticking with these, I haven't found a compelling reason to continue using the `function` keyword. Other than where it’s required, of course, such as in a framework. – chharvey Jul 11 '20 at 16:09
  • 1
    To me, not having generator arrow functions is as bad as not having async arrow functions. – David Callanan Sep 12 '22 at 15:15
167

The difference between Inline-functions and Arrow-functions

First of all Arrow-functions () => {} are not made to replace Inline-functions function(){} and they are different. Inline-Functions are simply Functions, so the question is what the difference between Arrow-functions and Inline-Functions are.

An arrow function expression (also known as arrow function) has a shorter syntax compared to function expressions and does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous.

Some more quick details here


Why Arrow-function can not be used as generators

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Use of the yield keyword

The yield keyword may not be used in an arrow function's body (except when permitted within functions further nested within it). As a consequence, arrow functions cannot be used as generators.

Note that generators without yield don't make sense.


Why Arrow-function can not use yield

http://tc39wiki.calculist.org/es6/arrow-functions/

Arrow functions bind this lexically, bind return in the Block body case so it returns from the immediately enclosing arrow function, and preclude break and continue from referencing statements outside the immediately enclosing arrow function.

The Identifier primary expression arguments may not be used in an arrow function's body (whether expression or block form).

Likewise, yield may not be used in an arrow function's body. Arrows cannot be generators and we do not want deep continuations.

Yield in an Arrow-Function will throw Semantic Error: http://www.ecma-international.org/

In the End the reason is in the deep complexity in the implementation of ECMA6. C# does not allow this as well for somewhat similar reasons.

Community
  • 1
  • 1
CoderPi
  • 12,985
  • 4
  • 34
  • 62
  • Would you please elaborate on why that is, and/or include explanation of why that `()=>{}` comparison is relevant? Because I don't understand yet why this limitation. – vitaly-t Dec 05 '15 at 16:47
  • 3
    I used a search engine and posted one more Explanation for you – CoderPi Dec 05 '15 at 16:52
  • 1
    I still think that adding explanation about `()=>{}` would help a lot, to understand its difference from an in-line function, and why the limitation is there for generators. – vitaly-t Dec 05 '15 at 17:44
  • An arrow function expression (also known as fat arrow function) has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous. – CoderPi Dec 05 '15 at 17:45
  • 100
    I'm trying to figure out why `*() => { yield bla; }` isn't ok, but `async () => { await bla; }` is... – Lee Benson Sep 20 '16 at 13:17
  • 10
    @CodeiSir, Re "*and we do not want deep continuations*", lousy excuses. – Pacerier Mar 20 '17 at 10:06
  • 1
    TC39 often make bad decisions like this – user9993 Jul 07 '18 at 10:06
  • 37
    Your argument is cyclical. You say that arrow functions can't be generators because they can't have the yield keyword in them. But they can't have the yield keyword, because they can't be generators: "Arrows cannot be generators and we do not want deep continuations." – Thayne Apr 18 '19 at 16:38
  • 5
    you don't answer why. Just said that they can't, but why? – Nugzar Gaguliya Jan 23 '20 at 10:57
  • 16
    That's circular reasoning; An arrow function can't be a generator, because it's not allowed to have a `yield` statement, and it can't have a `yield` statement, because it's not allowed to be a generator. – Sapphire_Brick Jun 10 '20 at 22:36
  • 11
    This doesn’t really answer *why*. Yes it's true, an arrow function can't be a generator because it isn't allowed to contain `yield`, but there's no reason why the syntax could have been designed to allow it. What is the reason the designers didn't want arrow functions to be able to be generators? – chharvey Oct 14 '20 at 03:29
  • @chharvey feel free to do some additional research, I would be interesting to see how deep the decision making was. And thanks for the downvote, you are welcome :D – CoderPi Oct 14 '20 at 11:09
  • 1
    Saying "arrow functions bind `this` lexically" is kind of misleading: rather, they do not implicitly _introduce_ a local `this` like non-arrow functions do. As a result, any reference to `this` binds to the one introduced by the innermost enclosing non-arrow function scope, exactly the same as how any reference to `x` binds to the variable with that name from the innermost scope that introduces one. – Matthijs Jun 26 '21 at 04:55
  • @LeeBenson Exactly - `async` functions are kind of subset of generators that generate Promises, you can see such transformation in [Babel](https://www.npmjs.com/package/@babel/plugin-transform-async-to-generator) – atablash Jun 21 '22 at 10:42
60

In addition to the discussion on esdiscuss.org and the Ecma TC39 committee ES6 meeting notes from November 2013 mentioned above, generator arrows were revisited in two September 2016 ES7 meetings [1] [2]. After a discussion about pros and cons of various syntax (mainly =*> and =>*) and a lack of justifications and use cases for this feature, they came to the conclusion that:

  • There is some interest from the committee, but concern that the feature does not pull its weight for adding a new piece of syntax
  • Plan to revisit on Day 3 to see if we can get =>* to stage 0 at least, as part of [Domenic Denicola]'s async iteration proposal

The proposal for generator arrows was moved to Stage 1 with Brendan Eich and Domenic Denicola as champions. Asynchronous iteration mentioned above was finished and implemented in 2018.

In Oct 2019 an official repo by Sergey Rubanov appeared with more discussion about syntax and other details.

monk-time
  • 2,123
  • 1
  • 14
  • 17
12

I was also having the same question and came here. After reading the posts and comments, I felt using generator in an arrow function seems to be vague:

const generator = () => 2*3; // * implies multiplication
// so, this would be a confusing
const generator = () =>* something; // err, multiplying?
const generator = () =*> ... // err, ^^
const generator = ()*=> ... // err, *=3, still multiplying?
const generator=*()=> ... // err, ^^
const generator = *param => ... //err, "param" is not fixed word

This is what may be the big reason they didn't implement generator in relation with arrow function.


But, if I were one of them, I could have thought like this:

const generator = gen param => ... // hmm, gen indicates a generator
const generator = gen () => ... // ^^

This feels just like we have asynchronous function:

const asyncFunction = async () => ... // pretty cool

Because, with normal function the async keyword exist, so arrow function is utilizing it - async () => is likely to seem async function().

But, there's no keyword like gen or generator and alas arrow function is not using it.

To conclude:

Even if they wish to implement the generator in the arrow function, I think they need to re-think about generator syntax in core js:

generator function myfunc() {}
// rather than
function* myfunc() {} // or, function *myfunc() {}

And this will be a big blunder. So, keeping arrow function out from the generator, is pretty cool.


Following @Bergi comment:

No. Arrow functions are supposed to be light-weight (and don't have a .prototype for example) and often one-liners, while generators are pretty much the opposite.

I will say that generator purpose to use is run-stop-run and so I don't think we need to care about prototype, lexical this, etc.

Bhojendra Rauniyar
  • 83,432
  • 35
  • 168
  • 231
  • 12
    Could also consider exotic options as well, like `() ~> { yield 'a'; yield 'b'; }`. To be honest I just love tildes. – Gershom Maes Oct 30 '19 at 15:44
  • 5
    @Gershom This is how programming languages like [Perl](https://en.wikipedia.org/wiki/Perl) go totally wrong – Sapphire_Brick Jun 10 '20 at 22:52
  • 2
    I find the solution `const someGeneratorFunction = gen () => ...` quite pretty. –  Jul 04 '22 at 19:14
  • I think the most mnemonic syntax would be `=>=>`, because a normal function produces one value so it's arguments `=>` [logic to get the return value], while a generator produces potentially multiple values, and because a generator goes, stops, then goes again. – mtraceur Dec 07 '22 at 06:37
9

Right now you can not, but in future you might be because TC39 release proposal for same in october 2019, which is in stage 1.

Gourav Makhija
  • 710
  • 7
  • 16
3

I know that this is very late, but another possible reason could be syntax. maybe (*() => {}) works, but what about (9 ** () => {})? Is that 9 to the power of an arrow function, returning NaN, or is it 9 times a generator arrow function, also returning NaN? It could be done with some alternative syntax, like =>* as mentioned by another answer here, but maybe there was a desire to preserve the consistency of the generator function syntax (eg. function* () {} and { *genMethod() {} }) when it was being implemented. Not too much of an excuse, but a reason for it.

coolreader18
  • 701
  • 9
  • 14
  • 1
    :+1: for the double asterisks... Old school JS guy here. Who says you can't teach an old dog new tricks :joy: – Shanimal Oct 12 '18 at 20:56
  • The only reason they don't do it, is because making the parser is difficult. It's entirely possible and requires no compromise in syntax. – Sophie McCarrell Mar 01 '19 at 21:31
  • @JasonMcCarrell If they cared enough about not making parser too complex, then maybe Brendan Eich should have put Scheme in the browser. – Sapphire_Brick Jun 10 '20 at 22:48
3

You can, but not really in a nice way. It's not shorter and does not look that pretty. Check this out:

function* iterable(arg) {
    yield* [];
}

async function* asyncIterable(arg) {
    yield* [];
}

const arrowIterable = arg => {
    return {
        *[Symbol.iterator]() {
            yield* [];
        },
    };
};

const arrowAsyncIterable = arg => {
    return {
        async *[Symbol.asyncIterator]() {
            yield* [];
        },
    };
};

This works because an iterable is basically an object with the Symbol.iterator or Symbol.asyncIterator set to an iterator. A generator is an iterator!

Enjoy!

Elmer
  • 9,147
  • 2
  • 48
  • 38
-7

There is a nice workaround with redux-saga

import { call, all } from 'redux-saga/effects';

function* gen() {
   yield all([].map(() => {
      return call(....);
   }));
}