> Was very glad to see them come to python and soon to mainstream javascript.

I really need to get around to writing a blog post to explain this in detail since this misapprehension is endemic. Python and JavaScript do not have coroutines, they have generators. Lua has actual coroutines.

The latter is dramatically more expressive than what you can do with what Python, JavaScript, and C# offer. This mistake drives me crazy because it means people don't know what they're missing.

Here's a quick example. Let's say we've got a little Python class for binary
trees:

class Tree:
def __init__(self, left, data, right):
self.left = left
self.data = data
self.right = right

We'll add a method to do an in-order traversal. It takes a callback and invokes the callback for every data value in the tree, like so:

def walk(self, callback):
"""Traverse the tree in order, invoking `callback` on each node."""
if self.left:
self.left.walk(callback)

callback(self.data)

if self.right:
self.right.walk(callback)

We can create a little tree and then print the data items in order like so:

tree = Tree(Tree(Tree(None, 1, None), 2, Tree(None, 3, None)), 4, None)

tree.walk(print)

(This works in Python 3, in Python 2, you'll have to make a little fn for print.) Swell, right?

Later, we decide we want to iterate over the items in a tree. Easy-peasy, Python has generators! We can just make a function that takes a Tree and returns a generator. We already have a method to walk the nodes, so we just need to call that and then yield the items, like so:

def iterateTree(tree):
def callback(data):
yield data

tree.walk(callback)

Then you can just use it like so:

for x in iterateTree(tree):
print(x)

Perfect, right?

Actually, no. This doesn't work at all. You can't yield from the callback passed to walk. That's because walk() itself doesn't know that the callback is a generator.

This is the problem with generators: they divide all functions into two categories: regular functions and generators. You run a regular function by calling it. You run a generator by iterating over it. The caller must use it in the correct way.

At its simplest level it means you have to be careful when refactoring. If you have a generator function that gets too big and you want to split it up, you have to remember that the functions you split out are also special generator functions if they contain a yield. You have to remember to flatten it when you "invoke it".

It's more than just annoying though: it means it's impossible to write code that works generically with both kinds of functions. In other words, all of your higher-order functions like map, filter, etc. now only work with some of your functions. (Or, I suppose, you could explicitly implement them to support both but that's more work and I don't think most languages do.)

In languages like Lua, the above code just works. You can yield from anywhere in the callstack and the entire stack is suspended. It's fantastic.

(If I can be forgiven a bit of self-promotion, I'll note that my programming language Wren[1] can not only express full coroutines like Lua, but also supports symmetric coroutines which can express some things Lua cannot. They are roughly like the equivalent of tail call elimination for coroutines.)

[1]: https://github.com/munificent/wren