Skip to content

Coroutines As A Core Language Feature #4

@weswigham

Description

@weswigham

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.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions