Free email newsletter: “ES.next News

2014-08-05

How should I format the ECMAScript 6 generator asterisk?

[Update 2015-03-30] This blog post is now a section in “ES6 generators in depth”.

The asterisk (*) is used by ECMAScript 6 to mark generator-related constructs [1]. In each case, you have considerable freedom w.r.t. adding or omitting whitespace before and after this character. This blog post explains how to best format the asterisk and why.

The generator asterisk

The generator asterisk appears in three locations in ECMAScript 6:

  • Generator function declarations and expressions:
    function* generatorFunc(x, y) { ... }
  • Concise generator method definitions [2]:
    { * generatorMethod(x, y) { ... } }
  • The recursive yield operator:
    yield* anotherGenFunc(123)

Reasonable – and legal – variations of formatting the asterisk are:

  • A space before and after it:
    function * foo(x, y) { ... }

  • A space before it:
    function *foo(x, y) { ... }

  • A space after it:
    function* foo(x, y) { ... }

  • No whitespace before and after it:
    function*foo(x, y) { ... }

Let’s figure out which of these variations make sense for which constructs and why.

Generator function declarations and expressions

Here, the star is only used because generator (or something similar) isn’t available as a keyword. If it were, then a generator function declaration would look like this:

    generator foo(x, y) {
        ...
    }

Instead of generator, ECMAScript 6 marks the function keyword with an asterisk. Thus, function* can be seen as a synonym for generator, which suggests writing generator function declarations as follows.

    function* foo(x, y) {
        ...
    }

Concise generator method definitions

When writing a concise generator method definitions, I recommend to format the asterisk as follows.

    let obj = {
        * generatorMethod(x, y) {
            ...
        }
    };

There are three arguments in favor of writing a space after the asterisk.

First, the asterisk shouldn’t be part of the method name. On one hand, it isn’t part of the name of a generator function. On the other hand, the asterisk is only mentioned when defining a generator, not when using it.

Second, a concise generator method definition is an abbreviation for the following syntax. (To make my point, I’m redundantly giving the function expression a name, too.)

    let obj = {
        generatorMethod: function* generatorMethod(x, y) {
            ...
        }
    };

If concise method definitions are about omitting the function keyword then the asterisk should probably be followed by a space.

Third, generator method definitions are syntactically similar to getters and setters (which are already available in ECMAScript 5):

    let obj = {
        get foo() {
            ...
        }
        set foo(value) {
            ...
        }
    }

The keywords get and set can be seen as modifiers of a normal concise method definition. Arguably, an asterisk is also such a modifier.

Recursive yield

The following is an example of a generator function yielding its own yielded values recursively:

    function* foo(x) {
        ...
        yield* foo(x - 1);
        ...
    }

The asterisk marks a different kind of yield operator, which is why the above way of writing it makes sense.

Documenting generator functions and methods

Kyle Simpson (@getify) recently proposed something interesting: Given that we often append parentheses when we write about functions and methods such as Math.max(), wouldn’t it make sense to prepend an asterisk when writing about generator functions and methods? For example: should we write *foo() to refer to the generator function in the previous subsection? I’d argue against that.

When it comes to writing a function that returns an iterable, a generator is only one of the several options. I think it is better to not give away this implementation detail via marked function names.

Furthermore, you don’t use the asterisk when calling a generator function, but you do use parentheses.

Lastly, the asterisk doesn’t provide useful information – yield* can also be used with functions that return an iterable. But it may make sense to mark the names of functions and methods that return iterables (including generators). For example, via the suffix Iter.

Further reading

[1]: Iterators and generators in ECMAScript 6 [2]: Callable entities in ECMAScript 6

No comments: