A function is more useful than an arbitrary string of Lua code or a global parameter callback because a function can be a closure capturing dynamically created objects that are not easily reachable from global variables.
E.g. I could have a factory function that instantiates an object, defines local functions that control and/or query that object, and then create parameters and watches with those local functions. My app can call that factory function as many times as needed, creating watches and controls for each instance.
@Nat regarding passing watch() an arbitrary string: I believe you could just define the function as a string - then it gets evaluated as a “chunk,” like any other function. And can manipulate the global environment, define functions and so on.
I can see the benefit of a parameter function over a global callback.
But watch() taking a stringified Lua chunk gives you the same functionality as a function, except the function object is optional. At least, I think this is the case. Correct me if I’m wrong.
A string of code can only refer to globals, but a function can refer to locals in it’s lexical scope. That’s really handy when creating a dynamic number of things you want to watch or control. And to avoid sticking more stuff into the global scope which makes the code harder to follow I find.
I think loadstring just loads a string of code as a Lua “chunk”. As far as I’m aware, local variables, lexical scoping rules, and the global environment are exposed when Lua executes a chunk.
Watch would simply use this mechanism to load and execute the chunk every frame and display the value left on top of the stack (usually the return value).
When evaluating a Lua chunk, the code can use the local variables of the environment in which the chunk is evaluated. The environment must be set up by the code evaluating the chunk. Usually the chunk is evaluated in the global environment.
A function, being a closure, can access variables in the environment in which the function is defined. That means that the program has control over that environment. It can be a local environment of a function activation, for example, which means that the controller cpuld easily be made to control objects that are created dynamically and never stored in global variables.
E.g. here’s an example of what I’d like to do:
function createNewDisc(nameOfThing)
local disc = Disc {name=nameOfThing, initialRadius=15}
parameter("Radius of " ..nameOfThing, 10, 20, function(newValue)
disc.radius = newValue
end)
return disc
end
and elsewhere in the program…
for i = 1, playerCount do
players[i] = createNewDisc("Player "..i)
end
I would find it even more useful with watches, for debugging dynamically created objects:
function createNewDisc(nameOfThing)
local disc = Disc {name=nameOfThing, initialRadius=15}
watch(nameOfThing .. " Position", function()
return disc.pos.x .. ", " .. disc.pos.y
end)
return disc
end
Ah I see. It makes sense that a chunk is not a closure. For some reason I thought it would be possible.
I don’t see why we can’t allow both. Strings would be evaluated with loadstring and functions could be used as you describe. I only thought that the string option could supplant functions. But closures are extremely useful, so it’s worth adding.