Wednesday, October 7, 2015

Future JavaScript - Generator Comprehensions

A comprehension generator?
If you enjoyed my look ahead at a JavaScript feature of the future on Monday, then you're in luck. If not, well, please read the rest of this post anyway.

That's right, I'm going to talk about one more upcoming language feature likely to be part of the ECMAScript 7 specification. If you like generator functions and you like array comprehensions, you're going to love Generator Comprehensions!


Not just another weird mashup, Generator Comprehensions have a syntax almost identical to Array Comprehensions, but create simple generator functions based on iterable objects instead of new arrays. Generator functions, if you recall, are a new type of JavaScript function which exit and re-enter while maintaining their state. If your generator function needs are very simple, creating one with a comprehension may be desirable.

Take a look at the simplest of examples to understand how they work. Important note! Just like with Monday's Array Comprehension examples, these only work on Firefox 30+. Sorry Chrome/Node fans.

var data = [ 1, 2, 3, 4, 5 ];

var gen = ( for (n of data) n * 2 );

console.log(; // prints 2
console.log(; // prints 4
console.log(; // prints 6

The Generator Comprehension appears on line 3. It looks almost exactly like an array comprehension, but uses parentheses ( ) instead of square braces [ ] to delineate it. Inside the parentheses, it takes the form for (n of iterable) statement, where the iterable in this case is an array and the statement is multiplying n by 2. The generator created will yield the result of the statement with the next value of n each time it is invoked. It is equivalent to this code:

var data = [ 1, 2, 3, 4, 5 ];

function* doubler(a) {
    for (var i = 0; i < a.length; i++) {
        yield a[i] * 2;

var gen = doubler(data);

console.log(; // prints 2
console.log(; // prints 4
console.log(; // prints 6

I suppose you'd have to agree that the comprehension version saves a few lines of code. And fewer lines means fewer chances to make a mistake, right? Maybe. But remember, if you overuse all this slick, super-short syntax, your code becomes less readable and therefore less maintainable. Trade-offs.

I should also mention that just like Array Comprehensions, the statement part of the expression can include an if. So if you wanted a generator that produced just the leap years from a given array of years, it might look like this:

var years = [ 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 ];

var leapYears = ( for (y of years) if ( y % 4 == 0 && (y % 100 != 0 || (y % 100 == 0 && y % 400 == 0)) ) y );

console.log(; // prints 1996
console.log(; // prints 2000
console.log(; // prints 2004

Of course, cramming the leap year logic (divisible by 4 but not divisible by 100 unless also divisible by 400) into a single if statement just trades vertical code for horizontal code - and horizontal code really bothers me. Call me old-fashioned, but I like it when the lines are no more than 80 characters wide. But everyone is entitled to their own code style - unless you have to work on a team! Then you better do what's agreed upon by the group. Just sayin'.

Now for some commentary. While Generator Comprehensions are a fine way to create generators in a single line of code, I feel like the real power of generator functions is in having them encapsulate logic that can not (or should not) be expressed as a single line. If I just want to iterate over an array and even perform some basic translation of its values, I'm not sure that a Generator Comprehension would be my first choice. At the risk of sounding like a software dinosaur, there's nothing wrong with for loops when done correctly. Their intent is more clear, which makes maintenance less costly. I worry about those kinds of things because I'm a software engineer, not a hacker.

I'm also an author of frog comics, which means I worry about you clicking on the link below. comic for 7 October 2015