Metatables and Black Magic
Metatables are Lua's secret weapon. They allow you to override fundamental operations on tables — addition, comparison, indexing, even function calls. With metatables, Lua's syntax becomes a canvas for creating domain-specific languages.
The __index Metamethod
The __index metamethod intercepts access to missing keys. It's the foundation of inheritance in Lua:
local Animal = {}
Animal.__index = Animal
function Animal.new(name, sound)
local self = setmetatable({}, Animal)
self.name = name
self.sound = sound
return self
end
function Animal:speak()
print(self.name .. " says " .. self.sound)
end
local cat = Animal.new("Cat", "meow")
cat:speak() -- "Cat says meow"
Operator Overloading
local Vector = {}
Vector.__index = Vector
function Vector.new(x, y)
return setmetatable({x = x, y = y}, Vector)
end
function Vector.__add(a, b)
return Vector.new(a.x + b.x, a.y + b.y)
end
function Vector.__tostring(v)
return string.format("(%g, %g)", v.x, v.y)
end
local v = Vector.new(1, 2) + Vector.new(3, 4)
print(v) -- (4, 6)
Proxy Tables with __newindex
Create read-only tables, validation layers, or observable objects by intercepting writes:
local function readonly(t)
return setmetatable({}, {
__index = t,
__newindex = function()
error("Attempt to modify read-only table")
end
})
end
The __call Metamethod
Make any table callable like a function. This enables factory patterns, decorators, and functional programming idioms:
local Logger = setmetatable({}, {
__call = function(self, msg)
print("[LOG] " .. os.date() .. ": " .. msg)
end
})
Logger("System initialized") -- Table used as function
Metatables blur the line between data and behavior. Use them wisely, and Lua bends to your will.
