luametaprogramming

JavaScript's proxy.apply handler in Lua


In JavaScript, you can make an object that is callable like this:

const prox = new Proxy(function() {}, {
  get(target, key) { return true }
  apply(target, that, argList) { console.log('This came from a proxy') }
})
prox() // This came from a proxy
console.log(prox.name) // true

I'd like to have something similar to that for a function that I am working on that has a method available to call instead.

local function describe()
  -- ...
end
function describe.skip()
  -- ...
end

This doesn't work because a function isn't a table.

Ideally this would be possible:

local describe = {}

function describe.__apply()
  -- ...
end
function describe.skip()
  -- ...
end

describe()
describe.skip()

Solution

  • A function may not behave like a table, but a table may behave like a function through the __call metamethod.

    A __call metamethod is invoked whenever the associated table is called as if it were a function. For example:

    local describe = setmetatable({}, {
        __call = function (self)
            print('describe() called', self)
        end
    })
    
    function describe.skip()
        print('describe.skip() called')
    end
    
    describe()
    describe.skip()
    
    describe() called   table: 0x5570496f0890
    describe.skip() called
    

    Additionally, __index and __newindex may be used to create complete proxies.

    See also PIL 13.4.4 – Tracking Table Accesses, which discusses proxy tables.