-
Notifications
You must be signed in to change notification settings - Fork 1
Coroutines As A Core Language Feature #4
Description
Co-routines are baby threads. Or perhaps super threads. In effect, they are threads whose scheduling is undecided. Python and Lua both implement co-routines at a language level, and for good reason. In python, they are used as an extension to generators, whereas in Lua they are used to model more complex iterator patterns and to handle concurrency issues in non-concurrent systems.
So, more to the point, the language should probably implement co-routines at the language level (for syntax prettiness purposes). A co-routine is a superclass of a function; any function is at least a one-step co-routine. What makes a co-routine different from a generator is that it can consume new inputs on every step (rather than just yielding outputs).
Since Brick is statically typed, co-routines may yield a problem - a co-routine may not want to yield the same type in all cases, and determining if the caller is expecting the correct ones can be difficult.
So, on to the actual syntax. In Python, coroutines were kind of hacked onto generators, so I'm going to ignore its ugly syntax for them:
def foo():
for i in range(10):
yield i # generator
def bar():
state = 1
for i in range(10):
state += (yield state) # coroutine
def main():
while True:
print(bar.send(math.random(0,20)))
Lua's syntax is a bit less unnatural, but still cumbersome.
function bar()
local state = 1
for i=1,10 do
state = state + coroutine.yield(state)
end
end
local core = coroutine.wrap(bar)
function main()
while true do
print(core(math.random(0, 20)))
end
end
(Lua has some more functions, such as resume, if you want to avoid the wrap shortcut)
So, what variety of coroutine syntax would fit in well with the language...?
I think something like
fn bar ->
let x = 1
10.times ->
x += ^.receive(x)
fn main ->
while true ->
puts(bar.send(math.random(0, 20)))
Which has none of the yield-keyword-ambiguity that python has, while avoiding the high verbosity of lua's coroutines. Additionally, using ^ to store a function's state (if we view a function has a state machine) makes sense in this context, also making coroutine 'trampolining' (yielding all the way down to the initial thread/scheduler so it can schedule/start the next task) un-needed, since the child can simply go
^^.send(result)
(This is actually one of the major problems in a coroutine-based system, the need to trampoline back down to the scheduler to pass inputs around. Being able to avoid that is pretty cool.)