Watches and Parameters

A suggestion for watches and parameters: allow function as well as string arguments.

  • If a watch is created with a function argument, the function is called to get the value to display.
  • If a parameter is created with a function argument, it is called with the parameter’s value whenever the user changes the parameter.

Using closures, this would let me watch or control dynamically created objects.

Would that be feasible? If so, I’ll raise an issue in the issue tracker.

@Nat I like the parameter function idea. Alternatively we could have a global

parameterChanged( paramName, value )

function that gets called whenever any parameter changes?

With watch() I was thinking to re-write it so that it can take and evaluate any arbitrary Lua string.

I like the idea of rewriting the watch() function @Simeon. I kind of want it.

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

I’m not very good at explaining this.

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 
    return disc 

and elsewhere in the program…

for i = 1, playerCount do
    players[i] = createNewDisc("Player "..i)

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
    return disc 

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.