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.