Lua - Proxy Table with metatables
A proxy table can be defined as a table which is not storing actual data as it is representing to store. A proxy table is an intermediary which delegates operations to another table or another object. We can even choose to do some logic instead of storing data.
A proxy table concept becomes powerful with the use of metatables. As a metatable is to intercept operations, we can set specific metamethods for proxy table operations when any operation or interaction happens with a proxy table.
Working of Proxy Table with Metatable
As Step 1, create a Table as an empty table. This table will act as a proxy table for our main table.
As Step 2, Create a metatable for our proxy table. Metatable responsibility will be to have metamethods to define the proxy's behavior.
As Step 3, Implement the metamethods like following −
__index − handle read access. This method is called when a key is not found in proxy table. Using this method, we can do activities like−
Retrieve underlying value from our main table.
Log entries.
Perform access control.
Compute and return a dynamic value.
Return a default value.
__newindex − handle write access. This method is called when a new key is added to proxy table. Using this method, we can do activities like−
Store value in our main table.
Prevent modifications to the table, making it readonly.
Perform validation on the value to be added.
Trigger side effects if required.
Similarly other methods like __call, arithmetic operations like __add, logical operations like __eq or __tostring can be implemented to customized behavior of our proxy table.
Finally as Step 4, set the metatable using setmetatable(proxy, metatable) to associate metatable with proxy table to assign custom behavior to the proxy> table.
Example - Create a Proxy for a Read-Only table
We can create a proxy to read data from a table by implementing __index, while preventing any modification to the underlying table by use of __newindex making it effectively readonly.
main.lua
-- underlying real data
local actual_data = { name = "Julie", age = 30 }
-- an empty proxy table
local proxyTable = {}
-- metatable to prevent write access to actual_data table via proxy table.
local metatableReadOnly = {
__index = actual_data,
__newindex = function(table, key, value)
error("Attempt to modify a read-only table", 2)
end
}
-- set metatable for the proxy table
setmetatable(proxyTable, metatableReadOnly)
-- read name using proxy table, prints Julie
print(proxyTable.name)
-- try to modify age, throws error
proxyTable.age = 31
Output
When we run the above program, we will get the following output−
Julie lua: main.lua:21: Attempt to modify a read-only table stack traceback: [C]: in function 'error' main.lua:11: in metamethod 'newindex' main.lua:21: in main chunk [C]: in ?
Key Considerations
Proxy tables combined with metatables is a powerful mechanism to control and customize unlying table behavior.
__index and __newindex are two fundamental metamethods to intercept read/write access to the table.
Using proxy tables we can achieve read-only access, lazy loading to load data when required, perform validation, delegation and tracing of events.