Coroutine question

I’m trying to get to grips with coroutines, but not getting very far. Can anyone work out why this crashes at the end of the routine, rather than triggering close()? Thanks in advance

-- Coroutine experiment

function setup()
    local walkLeft=function(x, tx)
        while x>tx do
            x = x - 1
            coroutine.yield(x)
        end
    end
    
    cor = {
    coroutine.create(walkLeft)}
    x = WIDTH*0.5
    tx = WIDTH*0.25
    y=HEIGHT*0.5
    fill(60, 175, 211, 255)
    strokeWidth(5)
end

function draw()
    background(40, 40, 50)
    local ok, x = coroutine.resume(cor[1], x, tx)
    if ok then
        ellipse(x, y, 50)
    else
        close()
    end

end

This is only a guess, but it’s probably because WalkLeft is local to setup, try defining the function outside of setup and seeing if that makes a difference.

No, that’s not it (it was originally outside of setup). I think I’ve worked it out, but I don’t quite understand why. If you add return x right after the while loop ends, then it operates as it should. So I guess that means on the final run through of the while loop, it yields the final x, then resumes to the end of the function, then returns one final true? I expected that after the while loop ended it would return false

Maybe I’m wrong, but I suspect it’s ellipse function who crash the process, not the coroutine. When a coroutine end it’s body fn, it return true plus the returned value(s) of the body fn (if no errors append). In your example, you check for “ok” and draw ellipse if it’s true, so when the coroutine end, ok will be true and x nil. I can’t test by myself but try to add the sate of the coroutine to the test. ie: if “dead” > close()

@toffer yes, sorry if my original post wasn’t clear, it is ellipse that crashes with the x value being invalid. So the final iteration of the loop returns “true, nil” (unless you put return x right after the while loop ends). I was hoping that the ok check might catch the error. I can’t check right now, but I don’t think a check to see if the Coroutine status is dead would catch the error either, because after the while loop ends the coroutine appears to return one final true.

This makes it work:

    local walkLeft=function(x, tx)
        while x>tx do
            x = x - 1
            coroutine.yield(x)
        end
        return x --new
    end

I’m a little surprised though that after the while loop ends the coroutine still returns one final true.

@Yojimbo2000 -I think the answer is that the coroutine returns true if there is no error, NOT if the coroutine is not finished. So on the last pass, it skips the loop and simply exits, but this is fine, there are no errors, so it still returns true.

If you had written the program so that the last pass did something useful, you would be upset if it didn’t return true.

So the True return has nothing to do with whether the coroutine has finished.

ok, I’ll try to be clearer. With the last return x, when the coroutine end, “ok” is equal to true and you run into an additional ellipse draw, in the next draw cycle ok is false and x is “cannot resume dead coroutine” so close is called. If the coroutine body don’t return x on it’s end, you’ll have to check the state of the coroutine because it will return true but not x.
Lua docs is our friend (coroutine.resume for the final returned true) http://www.lua.org/manual/5.1/manual.html#pdf-coroutine

ie: without additional return/ellipse call

local walkLeft = function(x, tx)
	while x>tx do
		x = x - 1
		coroutine.yield(x)
	end
end
...
function draw()
	background(40, 40, 50)
	local ok, x = coroutine.resume(cor[1], x, tx)
	if coroutine.status(cor[1]) == "dead" then
		close()	
	else
		ellipse(x, y, 50)
	end
end

I hope it make sense.

EDIT: @Ignatz I wish I could explain so well like you ! english is so terrible to me :s

@toffer - you do very well. I could not make any sense if I had to write in
French or German or… (And I wish I had half your technical skill!)

@Ignatz and @toffer already gave the answer, I just wanted to add that you could keep your coroutine function as is, and even avoid the status check in the draw, but instead verify that x is not nil, since that is what it will be after last resume.

function draw()
    background(40, 40, 50)
    local ok, x = coroutine.resume(cor[1], x, tx)
    if ok and x then
        ellipse(x, y, 50)
    else
        close()
    end
end

@toffer and @juce thank you, both of those methods work too. Here’s a modified version using coroutine.status as @toffer suggested that shows the cycle of states at the end of the routine.

--# Main
-- Coroutine Experiment

function setup()
    local walkLeft=function(x, tx)
        while x>tx do
            x = x - 2
            coroutine.yield(x)
        end
    end

    cor = {
    coroutine.create(walkLeft)}
    x = WIDTH*0.5
    tx = WIDTH*0.25
    y=HEIGHT*0.5
    fill(60, 175, 211, 255)
    strokeWidth(5)
    ok = true
end

function draw()
    background(40, 40, 50)
    if ok then ok, x = coroutine.resume(cor[1], x, tx)
        if coroutine.status( cor[1]) == "dead" then
            print ("Status = dead:")
            print (ok)
            print (x)
        else
            ellipse(x, y, 50)
        end
    end
end

--[[
prints:
Status = dead:
true
nil
Status = dead:
false
cannot resume dead coroutine
  ]]

So my mistake was assuming that the ok variable would be the same as the status, whereas in fact, as @Ignatz pointed out, ok only becomes false if there is an error, which in this case occurs on the next cycle when you try to “resume dead coroutine” . Thanks to all for your answers!

My eventual aim btw is to see if I can build a simple coroutine scheduler, similar in syntax to the tween.sequence command, for things like AI programming.