48

Can someone explain to me: why are generator functions in ES6 marked by asterisk symbol?

For example, instead of:

function *someGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

we could write:

function someGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

or even:

var someGenerator = () => {
    yield 1;
    yield 2;
    yield 3;
}

var someObject = {

    someGenerator() {
        yield 1;
        yield 2;
        yield 3;
    }
}            

The JS compiler can detect that someGenerator contains yield operator at the parse time and make a generator from this function.

Why is detection of yield existence not enough?

alexpods
  • 42,853
  • 9
  • 92
  • 91
  • I don't know much about ES6, but I do know that the asterisk isn't unique to the `function` keyword. - you can also use `yield*`. – James Donnelly Jan 05 '15 at 11:05
  • Read this https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* – Henrik Andersson Jan 05 '15 at 11:06
  • @JamesDonnelly Yes, you can use `yield*`. So my question is why compiler cant detect that function is generator by `yield` (or `yield*`) existence? – alexpods Jan 05 '15 at 11:16
  • @limelights - I red it, but didn't find answer to my question. Can you point to the paragraph where answer is? – alexpods Jan 05 '15 at 11:18
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield* – chovy Jan 10 '15 at 04:55
  • 1
    This is something that confuses me. From looking at docs on MDN it looks like the asterisk should be function* NOT function *. i.e. it goes immediately after function itself NOT the function name itself. Is there a difference? – JGFMK Jun 23 '17 at 20:11

2 Answers2

68

The three reasons were:

  1. Readability. A generator is quite different from a function, and the difference should be immediately visible (that is, without examining the whole implementation in search for a yield).

  2. Generality. It should be naturally possible to write generators that do not yield, and only return directly. Moreover, commenting out part of the body (e.g. for debugging) should not silently change whether something is a generator.

  3. Compatibility. Only strict mode reserved 'yield' as a keyword, but it was made a goal for ES6 that all new features are also available in sloppy mode (an unfortunate decision IMHO, but nevertheless). Moreover, even in strict mode there are many parsing subtleties around 'yield'; for example, consider default arguments:

    function* g(a = yield(2)) { 'use strict' }
    

    Without the *, the parser could only decide how to parse the yield after it has seen the body of the function. That is, you would need infinite look-ahead, or back-tracking, or other hacky techniques to deal with this.

I should note that (1) and (2) are already reason enough.

(Full disclosure: I am a member of the EcmaScript committee.)

Andreas Rossberg
  • 31,309
  • 3
  • 55
  • 70
  • Using the opportunity I want to ask: have ECMAScript committee any plans about array generators (something like `() *=> { }`) and generator methods of object literals (something like `var someObject = { *someGenerator() {} }`)? Because IMHO `function *` is so many symbols. And without array generators we must use `.bind(this)` or `var that = this` as in old es5 days. – alexpods Jan 05 '15 at 21:17
  • 3
    @alexpods, generator methods (`{ *g(){} }`) are already in ES6. Arrow generators were discussed more than once, but nobody was able to come up with a syntax that was consistent and didn't have problems. – Andreas Rossberg Jan 05 '15 at 21:34
7

Empty generators (with no body) are not disallowed; so should unStarredFunc() follow generator semantics or not?

For compatibility reasons:

function yield(x) { return x };

function a() { 
    yield (4+1);
};

this is syntactically correct but calling .next() would result in an error whereas adding an asterisk to explicitly define a generator would cause .next().value === 5

detect that someGenerator contains yield operator at parse time

Some constructs cannot be resolved at parse time:

function someGenerator(i) { 
    if (glob) 
        return 4; 
    else 
        yield* anotherGen(i);
}

And of course its simpler to see immediately from the function* definition that its a generator without needing to dig into its source to look for yields.

Alex K.
  • 159,548
  • 29
  • 245
  • 267
  • 1) For compatibility reasons - yes, I agree with it. But you can cover new realization under 'use strict'. If 'use strict' specified than `yeild` is interpreted as operator. Otherwise, if function with such name realized, - `yield` is interpreted as function, and you need add asterisk `*` to the function to make generator from it. – alexpods Jan 05 '15 at 12:55
  • Good answer. By the way, even a non-empty function can function as a generator with no `yield`s in it. It returns `done` on the first call to next with the value of the `return` statement as `value`. –  Jan 05 '15 at 13:00
  • 2) `Some constructs cannot be resolved at parse time` - actually your example is generator, and it can be resolve an parse time. Logic is simple - `yield` (or `yield*` exists) it generator. Otherwise - its function. After all, your nevertheless cannot create conditional function/generator function in current es6 realization. – alexpods Jan 05 '15 at 13:00
  • 3)`And of course its simpler to see immediately from the function* definition` - partially agree. By such languages as `php` (from 5.5), `python`, `ruby` have not asterisk symbol in their generators, and I didn't meet people who complain about it. – alexpods Jan 05 '15 at 13:03
  • @alex-k By the way - thanks, good answer. I'll wait awhile and, if no one answer better, mark it as appropriate. – alexpods Jan 05 '15 at 13:06
  • @torazaburo Yes, I agree. But you can consider my assumption as addition to current realization. So - if you have empty generator, you must specify `*`. Otherwise this is not necessary, if function contains `yield`. – alexpods Jan 05 '15 at 13:10
  • 1
    I take your points but examining strictness, looking for an existing function called `yield` (which could come to exist at any time) and special cases for empty generators all sounds way more complicated than a *just add an asterisk* approach. I think at root it can be said that `*` acts as a toggle for the reservedness & behaviour of `yield` in *new* code so that it will not cause any problems with any existing code. – Alex K. Jan 05 '15 at 13:26
  • 1
    I imagine this is why they chose a previously-code-breaking symbol decoration rather than creating a new *`Enumerable`* keyword – Alex K. Jan 05 '15 at 13:29