question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Treat generator stars like prefix operators.

See original GitHub issue

Prettier 1.19.1 Playground link

--parser typescript

Input:

export function *flattenChildren(children: Children): Iterable<Child> {
  if (typeof children === "string" || !isIterable(children)) {
    yield children;
    return;
  }

  for (const child of children) {
    yield *flattenChildren(child);
  }
}

Output:

export function* flattenChildren(children: Children): Iterable<Child> {
  if (typeof children === "string" || !isIterable(children)) {
    yield children;
    return;
  }

  for (const child of children) {
    yield* flattenChildren(child);
  }
}

Expected behavior: Input unchanged.

This is a small nit I have with prettier, which is that it puts a space after generator stars and no space before. I believe this is suboptimal because in all cases, these stars act like a prefix operator, which prettier in all other cases puts flush against its operand (!value, ++i). Treating generator stars like prefix operators is both more consistent and helps catch bugs.

Consistency

There are currently two places in javascript where stars can be used to create generator functions:

  1. after the function keyword in function declarations and expressions:
function *fizzbuzz() {
  /* ... */
}
  1. before method names in classes and object literals.
class FizzBuzzer {
  *[Symbol.iterator]() {
    /* ... */
  }
}
const fizzbuzz = {
  *[Symbol.iterator]() {
    /* ... */
  },
};

In the first case, it’s not exactly clear where the star goes and prettier places the star against the function keyword. In the second case, there’s nothing before the star, and even prettier puts the star against the method name. Why do we treat these two cases differently? Insofar as there is no “before” to put the star against in the case of class/object method declarations, we should prefer to put the star against function names as well for the purposes of consistency.

The one instance of inconsistency which this rule causes is the case of anonymous generator expressions.

(function*() {})();

Here it would seem that putting the star against the parameter list is inconsistent because while there is a space before the star in named functions, there isn’t a space before the star in anonymous functions. I concede this point, and find that it is further evidence that we should simply add a space after all function keywords consistently (see issue #3847).

In the case of yield stars, adding a star without an expression is simply a syntax error:

yield*;
//    ^ parser expects an expression

This is more evidence that generator stars should be treated like prefix operators.

Catching bugs

Function stars change the return value of functions and yield stars delegate yielding to the yielded expression. By putting these stars against the keywords function or yield, you increase the chance that a developer will miss that the function is a generator, or that the yield is being delegated. Programmers will often gloss over keywords like function or yield when reading code because they are common and unchanging, while the names of functions and the contents of yielded expressions are critically important to read and make sense of, if only to catch typos. Most syntax highlighters will also highlight stars the same color as the keyword function and yield, compounding the problem.

Consider this actual bug I have personally made, which cannot be type checked away:

function* getRawText(): Iterable<string> {
  /* some logic to get an iterable of tokens  */
  for (const token of tokens) {
    yield* token.getRawText();
  }
}

Because strings are themselves iterables of strings (where each iteration yields a character), a type checker would not notice that the developer was accidentally yielding each token‘s text character by character. However, this is almost certainly be a bug. By placing the star flush against the expression:

    yield *token.getRawText();

we make it more obvious that we are delegating to the expression, no matter how busy the expression becomes.

A similar argument can be made for function/method names. Generator functions are lazy, so it is critical that developers understand that a function returns a generator object and use the generator object in some way to execute the generator. By placing the star against the name of the function, we make it clear to readers that the function returns a generator object and executes lazily.

Possible Objections

I would argue in response that they aren’t keywords, and there isn’t a single example of a “keyword” which permits spaces between its members, or even has “members” to begin with. The keywords are function and yield, and while the stars modify the behavior of the keywords (they change the return value of a function or delegate yielding), they do not change the fact that we are declaring a function/yielding from a generator.

  • Putting a space after stars is just established “convention.”

I would argue that there is no clear consensus about how to space generator stars, and that any “conventions” were established before generators came to be used regularly. I use generators regularly in code I write, and I have provided two objective points as to why the convention should be as I described. In addition, there is the convention of prefix operators, and I believe I’ve established that stars are more similar to prefix operators than postfix operators (even though they are neither).

Therefore, I propose prettier uniformly place generator and yield stars flush against whatever follows, rather than whatever came before. This should be the default behavior, and not an option. This can be done if/when #3847 is done.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:6
  • Comments:12 (12 by maintainers)

github_iconTop GitHub Comments

4reactions
thorn0commented, Jan 15, 2020

We don’t do Twitter polls anymore. They are not really representative of the user base. I guess we just need to make sure there is no good reason for rejecting this proposal. If anybody knows an argument that can outweigh brainkim’s reasoning, let them write it now. Otherwise we’ll proceed.

I pinned this issue to bring additional attention to it.

@lipis Could you tweet a link to this issue without creating a poll?

@brainkim Would you like to make a PR if we decide to proceed?

2reactions
thorn0commented, Jan 15, 2020

@brainkim Okay, we’ll probably just include it in #3903, but it’s going to need a review from you.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Expressions And Operators: Yield
Any function containing the yield operator is a generator function. A generator function generates a collection of zero or more key/value pairs where...
Read more >
22. Generators - Exploring JS
You can think of generators as processes (pieces of code) that you can pause and ... methods). yield is an operator with which...
Read more >
Expressions and operators - JavaScript - MDN Web Docs
Chrome Edge Addition ( + ) Full support. Chrome1. Toggle history Full support. Edge12... Addition assignment ( x += y ) Full support. Chrome1. Toggle...
Read more >
Generator Arrow Functions - ES Discuss
It's a small point, but the rationale is "the star goes after the first token that identifies the special form as a function...
Read more >
Random Number And String Generator In Java - Edureka
Math.random(). The class named as Math comprises of various methods for performing a number of different numeric operations which includes ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found