Introducing Lyfe: Yield in JavaScript
[Please note the age of this post. Chances are that a lot of this content is outdated.]
I've been a fan of using yield
to create generators in Python
for a long time, and when I was dragged into the world of C#, I was thrilled to see that it supports this
pattern as well.
Generators with yield
The yield
pattern is a great way to “generate stuff” to be iterated over, and do so in a clear and
easily readable way. The basic conceptual idea is: You have a function that returns multiple times,
and all those different return values are just a collection of values to iterate over.
Blog posts that include code always need contrived examples that have nothing to do with the real world, so let's use FizzBuzz:
Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.
In Python, a generator that yields FizzBuzz “numbers” might look like this:
def infiniteFizzBuzz():
i = 1
while True:
word = ""
if i % 3 == 0:
word = "Fizz"
if i % 5 == 0:
word += "Buzz"
if word:
yield word
else:
yield i
i += 1
To print the first hundred of them, we do this:
from itertools import islice
for x in islice(infiniteFizzBuzz(), 100):
print x
In C#, the whole thing looks extremely similar. Note that we're taking the simple route of just yielding object
s, since we can have
both integers and strings as results; an enterprise version would of course have something like an IEnumerable<IFizzBuzz>
. Well, the “actual”
Enterprise FizzBuzz has ITransformer
to turn numbers into strings. Anyway; I digress.
public static IEnumerable<object> InfiniteFizzBuzz()
{
var i = 1;
string word;
while (true)
{
word = "";
if (i % 3 == 0)
word = "Fizz";
if (i % 5 == 0)
word += "Buzz";
if (word.Length > 0)
yield return word;
else
yield return i;
i++;
}
}
To again output one hundred, using a little bit of LINQ:
foreach (var x in InfiniteFizzBuzz().Take(100))
{
Console.WriteLine(x);
}
So what about JavaScript?
Tough luck. There's no yield
in JavaScript. Well, in Firefox there is,
if you give your <script>
tag a new type, namely "application/javascript;version=1.7"
. Chances are that sooner or later, yield
-based
generators will be part of the standard, but that's dreaming of the future for now.
Fully emulating this behavior in JavaScript in a way that all current browsers understand is not possible, because you don't have coroutines – there's no way to leave a function and later come back to continue where you left off.
But I really wanted to see yield
in JavaScript, so I tried playing around with some callback trickery, and came up with “Lyfe”, which is an incredibly
witty recursive acronym for Lyfe: yield for everyone.
Here's the above FizzBuzz example in JavaScript using Lyfe:
var infiniteFizzBuzz = Generator(function () {
var i = 1, word;
while (true) {
word = "";
if (i % 3 === 0)
word = "Fizz";
if (i % 5 === 0)
word += "Buzz";
if (word)
this.yield(word);
else
this.yield(i);
i++;
}
});
And here is the corresponding iteration:
infiniteFizzBuzz.take(100).forEach(function (val) {
console.log(val);
});
Watch out a little bit
Lyfe approaches the whole thing backwards: When encountering yield
, instead of leaving the generator function to essentially go down a level
in the call stack, instead yield
is a function call that creates a new level on top of the stack. Interestingly, Eric Lippert identifies
this method as a valid alternative in one of his blog posts on generators in C#.
It comes with a few drawbacks. For example, it's not possible to implement an iterator protocoll with this (think “next()
”), and
there are no syntax restrictions preventing you from, say, leaking yield
out of the current scope. I may, at some point, add a way to prevent
some weirdness at runtime, but currently, it's “just don't do it”.
The purpose of Lyfe (no pun intended) is not to make everything work identical to, say, Python; the purpose is to offer generator functions
with yield
, and a few tools to work with them.
The project is very new and experimental, but if you're interested, you can find it on BitBucket at bitbucket.org/balpha/lyfe. Be sure to
tell me what you think!
previous post: A shout-out to the people of Meta Stack Overflow
next post: Look, honey! I injected a dependency!
To see comments or leave your own, . Here is their privacy policy.
You can also find me on Mastodon.