Can you use a method as a variable?

It’s easy to use a function as a variable. This works fine:

function printTest (printMe)
    print(printMe)
end

function doThisToThis(functionA,target)
    functionA(target)
end

trial = "it works"

doThisToThis(printTest,trial)

Result: “it works” gets printed.

But when I add this:

function MyClass:methodPrint(printMe)
    print(printMe)
end

doThisToThis(MyClass:methodPrint,trial)

I get an error saying “function arguments expected near ‘,’”, next to the last line.

So then I try it this way:

doThisToThis(MyClass:methodPrint(),trial)

Which gives this error: “attempt to call local ‘functionA’ (a nil value)”, next to “functionA(target)” inside the “doThisToThis” function definition.

Is what I’m trying to do not possible? Or is there some hope in this cruel world after all? :slight_smile:

You have to write MyClass.methodPrint instead, otherwise it tries to parse it as you are trying to make a function call, it seems like. But you also have to call the method in the right context, so in your example the argument trial should be an instance of MyClass.

but but but–making trial an instance of MyClass ruins the whole point of having a function that can apply any given function to any given target. The parameters “functionA” and “target” in “doThisToThis” should be able to know nothing at all about each other, otherwise it’s just a weird way of having an object use one of its own functions on itself, no?

Thanks for helping, it’s great that people here are so responsive.

Another solution would be to bake the call into a function, if you want to call print on instance x for example.

doThisToThis(function(t) x:methodPrint(t) end, trial)

tnlogy: that’s a very clever way to do it.

Sure seems like you should be able to do it a less-circuitous way, though. But good work-around, thanks!

When passing a method, you also have to pass the object on which to call that method. I don’t know of any way around this.

function doThisToThisOnThat(functionA,target,object)
    functionA(object,target)
end

function MyClass:methodPrint(printMe)
    print(printMe)
end

MyInstance = MyClass()

doThisToThisOnThat(MyClass.methodPrint,trial,MyInstance)

Or

function doThisToThisOnThat(functionA,target,object)
    object[functionA](object,target)
end

function MyClass:methodPrint(printMe)
    print(printMe)
end

MyInstance = MyClass()

doThisToThisOnThat("methodPrint",trial,MyInstance)

Heh, I actually just wrote this today for something else that I’m going to be posting about sometime tonight, but I think it might be relevant to the discussion. A way that I’ve dealt with this is to create a function that returns a bound member function call. For example:


function bind(obj, methodname)
    return function(...) return obj[methodname](obj, ...) end
end

Now, whenever you need something to bind an object with it’s method for use in a callback, you can use:


-- add callback to example touch event manager
touchManager:addCallback(bind(myObj, 'touched'))

-- later on, touchManager calls our bound callback with a touch object as an argument:
callbacks[i](touch)

-- which will functionally equate to:
-- myObj:touched(touch)

I’ve even actually taken it a step further: because passing the same inputs to bind() will always generate functionally identical outputs, I memoize the bound method. The main reason that I did this was because I wanted a simple way to be able to compare bound methods for later removal from callback lists. For example:


local bound1 = bind(someObj, 'someFunc')
local bound2 = bind(someObj, 'someFunc')

print(bound1 == bound2)

Will print ‘false’, because even though the functions generated by bind() in this case are functionally identical, they are not equal because they are completely separate objects. With memoized bound methods, we only need to create the bound method the first time it’s needed; subsequent times, we’ll just return the one we created earlier. Now:

-- caller() is bind() but with memoization
local bound1 = caller(someObj, 'someFunc')
local bound2 = caller(someObj, 'someFunc')

print(bound1 == bound2)

Will print ‘true’. This is especially useful for event systems, where (especially in Lua) you might find yourself needing to keep a separate reference to your bound callback so that you can de-register it later. Using caller(), you don’t have to, since it memoizes the bound method. Now you can register and deregister the same way, with no additional reference tracking:


touchManager:addCallback(caller(myObj, 'touched'))
touchManager:removeCallback(caller(myObj, 'touched'))

I’ve also added another whistle to the caller() function, which is that the tables used for memoization keep weak references, so that once there are no more references to the object and bound method, they will be garbage collected automatically. Here is the full code for caller():

-- caller

local _weakKeyMT = {__mode = "k"}
local _weakValueMT = {__mode = "v"}
local _methods = setmetatable({}, _weakKeyMT)

function caller(obj, method)
    local methods, call = _methods[obj]
    
    if methods then
        call = methods[method]
    else
        methods = setmetatable({}, _weakValueMT)
        _methods[obj] = methods
    end
    
    if not call then
        call = function(...) return obj[method](obj, ...) end
        methods[method] = call
    end
    
    return call
end