lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Fri, Aug 12, 2016 at 11:25:21PM +0200, Stefan Reich wrote:
> Like Lua has. :-)
> 
> https://medium.com/@stefanreich/java-can-have-coroutines-e469dc91c15a
> http://www.ai1.lol
> 

Not really.

1) Coroutines as added to most languages require adorning the function
definition and/or invocation.

2) Coroutines in most languages are a special kind of function, thus point
#1. Coroutines in Lua are an abstraction around a thread. In Lua you can
yield from nested function calls, without intermediate function invocations
having to know about coroutine semantics. In most other languages you either
have to chain invocations with special decorators like await, or you have to
use a special function type. In both cases you're violating functions as
first-class values as now you have at least two entirely distinct function
types.

As a classic example, Lua's coroutines can be used to emulate green
threading. But just as importanly it means complex iterators are easier to
create because you can easily implement the logic of the iterator across
multiple functions, most or even all of which can be ignorant of coroutine
semantics. Whereas in other languages you're either going to have to
shoe-horn everything into a single function, or you're going to have to
mix-and-match regular functions with coroutine-specific functions.

That's not to say that the more limited form of coroutines in other
languages isn't useful. But they're not nearly as useful and convenient as
in Lua.

I can implement functions-as-coroutines in C using the preprocessor[1].
There are limitations to what you're allowed to do from the function. But
there are limitations to coroutines in other languages. And that's my point:
Lua's coroutines really stand out wrt their level of abstraction.


[1] Here's the most recent implementation I wrote, which is pure ANSI C.
I've used this pattern or something like it dozens of times, and it's so
simple it's not really worth turning into a library.

/*
 * Application code should define SM_RESTORE and SM_SAVE, and the
 * local variable pc.
 */
#define SM_ENTER \
	do { \
	static const int pc0 = __LINE__; \
	SM_RESTORE; \
	switch (pc0 + pc) { \
	case __LINE__: (void)0

#define SM_SAVE_AND_DO(do_statement) \
	do { \
		pc = __LINE__ - pc0; \
		SM_SAVE; \
		do_statement; \
		case __LINE__: (void)0; \
	} while (0)

#define SM_YIELD(rv) \
	SM_SAVE_AND_DO(return (rv))

#define SM_EXIT \
	do { goto leave; } while (0)

#define SM_LEAVE \
	leave: (void)0; \
	SM_SAVE_AND_DO(break); \
	} \
	} while (0)

If you can use computed goto extensions (supported by every major compiler
except Visual Studio), you can implement a faster version with fewer
limitations. One obvious limitation to the above is that you cannot yield
from a nested switch statement. But for implementing iterators (the purpose
for which cooroutines are most often sold in other languages) the above is
adequate for even complex cases. I also use the above in C code to simplify
using asynchronous I/O, another use case coroutines are used for in other
languages.