6

What is the purpose of passing arguments to lua_resume and lua_yield?

I understand that on the first call to lua_resume the arguments are passed to the lua function that is being resumed. This makes sense. However I'd expect that all subsequent calls to lua_resume would "update" the arguments in the coroutine's function. However that's not the case.

What is the purpose of passing arguments to lua_resume for lua_yield to return? Can the lua function running under the coroutine have access to the arguments passed by lua_resume?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
RandyGaul
  • 1,915
  • 14
  • 21

3 Answers3

10

What Nicol said. You can still preserve the values from the first resume call if you want:

do
  local firstcall
  function willyield(a)
    firstcall = a
    while a do
      print(a, firstcall)
      a = coroutine.yield()
    end
  end
end

local coro = coroutine.create(willyield)
coroutine.resume(coro, 1)
coroutine.resume(coro, 10)
coroutine.resume(coro, 100)
coroutine.resume(coro)

will print

1 1
10 1
100 1
Paul Kulchenko
  • 25,884
  • 3
  • 38
  • 56
  • 1
    I feel like this does not answer the question. The OP was about the /C API/ functions lua_resume() and lua_yield(), which have different semantics than the standard library `coroutine.resume()`, etc. (For example, lua_resume takes three arguments - two of them are lua_State...) – BadZen Jun 05 '16 at 17:09
  • I think it does answer the question about the purpose of passing parameters and provides an example that is easy to try having a Lua interpreter. In fact, OP said as much in his comment under Nicol's answer. You can always provide your own answer if you think you have a better one. – Paul Kulchenko Jun 05 '16 at 21:22
  • There is a big difference between the coroutine object in libs (which for example encapsulates a lua_State) and using the C API. I don't have a better answer, so I opened another question asking specifically about the C API. Nichol's answer and yours here both talk about the coroutine library object. There are about 100 lines of code that implement that in terms of the lua_* API. It's non-obvious, and the documentation is short on detail. – BadZen Jun 05 '16 at 23:40
5

Lua cannot magically give the original arguments new values. They might not even be on the stack anymore, depending on optimizations. Furthermore, there's no indication where the code was when it yielded, so it may not be able to see those arguments anymore. For example, if the coroutine called a function, that new function can't see the arguments passed into the old one.

coroutine.yield() returns the arguments passed to the resume call that continues the coroutine, so that the site of the yield call can handle parameters as it so desires. It allows the code doing the resuming to communicate with the specific code doing the yielding. yield() passes its arguments as return values from resume, and resume passes its arguments as return values to yield. This sets up a pathway of communication.

You can't do that in any other way. Certainly not by modifying arguments that may not be visible from the yield site. It's simple, elegant, and makes sense.

Also, it's considered exceedingly rude to go poking at someone's values. Especially a function already in operation. Remember: arguments are just local variables filled with values. The user shouldn't expect the contents of those variables to change unless it changes them itself. They're local variables, after all. They can only be changed locally; hence the name.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thanks! So to clarify, once a coroutine is started the local values cannot be modified externally? I'd assume I'd need to communicate to the internals of the coroutine by global variables then? For example I might have a count of "nearby objects" in a game, where the coroutine needs to know how many other objects are within a distance. In order to do this I'd have to have a global to store the number of nearby objects? I was hoping I could values to the coroutine directly just before I resume to avoid cluterring the namespace with globals. – RandyGaul Nov 16 '12 at 21:24
  • The values can be modified; you just need to assign the result of coroutine.yield() call to the same variable you passed as the parameter originally. Look at my answer for example. – Paul Kulchenko Nov 16 '12 at 21:29
  • @RandyGaul: "*once a coroutine is started the local values cannot be modified externally*" `local` variables cannot be modified externally *period*; coroutines are not special. If you want to pass this number, pass them as parameters to `resume` and collect them as the return value from `yield`. As I stated, that's the communication interface you have. – Nicol Bolas Nov 16 '12 at 21:29
  • I see, thanks a lot for the explanations, and thanks for the example code Paul! – RandyGaul Nov 16 '12 at 21:32
0

A simple example:

co = coroutine.create (function (a, b)
       print("First  args: ", a, b)
       coroutine.yield(a+10, b+10)
       print("Second args: ", a, b)
       coroutine.yield(a+10, b+10)
     end)
print(coroutine.resume(co, 1, 2))
print(coroutine.resume(co, 3, 4))

Prints:

First  args:    1       2
true    11      12
Second args:    1       2
true    11      12

Showing that the orginal values for the args a and b did not change.

run_the_race
  • 1,344
  • 2
  • 36
  • 62