Why metatables?

I’ve been reading/watching a few tutorials on how metatables work and I’m still wondering what the purpose of them is for?

local larry = {}
larry.skills = {speed = 20, strength = 200}

larry.metatable = {__index = larry.skills}

function larry:new()
    local a = {}
    setmetatable(a,larry.metatable)
    return a
end

local larry = larry:new()

print(larry.strength)

output prints 200

so from my understanding, metatables are basically tables being attached to other tables, so it’s easier to call values inside of a table that doesn’t have a value. But isn’t this a bit counterintuitive? why not just do it this way?:

local larry = {
	skills = {
		speed = 20;
		strength = 200;
	}
}

print(larry.skills.strength)

output prints 200

This the same outcome, but just more simpler. I keep hearing that metatables are the way to go on keeping your code clean and organized, but honestly it looks like a mess. Is there any reason why metatables work better?

1 Like

Metatables have special metamethods like __add which make scripting easier.They improve code readability and can act as syntax sugar

For the case of OOP.
Imagine something like this.

local larry = { }
larry.__index = larry

function larry.new()
    local new_larry = { }

    function new_larry:eat()
        -- eat
    end
    
    function new_larry:clean_plate()
        -- clean plate
    end
    
    setmetatable(new_larry, larry)
end

Now imagine you have hundreds of instances of larries.

You would also have hundreds of copies of functions. This is terrible for memory! So what is instead used is __index, so that you only get the value when it doesn’t exist so it isn’t a part of every table.

1 Like

In addition to what others have said

What I like to use metatables to share functions between scripts for easy editing, also making adaptive modules that can pretty much handle everything.

You can also index self, which allows you to easily store data and index it.

My most recent use of a metatable was in a wrapper module for the math library I’ve been working on.

I set the built-in math table as the metatable’s index so that when a user tries to access a function or constant that isn’t in the wrapper, it would search for it in the math library.

local module = {
	e = math.exp(1)
}

function module.isReal(n)
	return ((typeof(n) == "number") and (n == n) and (math.abs(n) ~= math.huge))
end

function module.isInt(n)
	return module.isReal(n) and (math.floor(n) == n)
end

function module.round(x, degree, base)
	assert(module.isReal(x), "Input must be a real number.")
	if (degree ~= nil) then
		assert(module.isInt(degree), "Degree must be an integer.")
	else
		degree = 0
	end
	if (base ~= nil) then
		assert(module.isInt(base), "Base must be an integer.")
		assert((base > 0), "Base must be greater than zero.")
	else
		base = 10
	end
	local n = base ^ degree
	return math.floor(x * n + 0.5) / n
end

function module.blahblahblah()
	-- etc.
end

return setmetatable(module, {__index = math})
local wrapper = require(mathmodule)
-- 'round' is a function in the module, 'pi' is in the math table.
print(wrapper.round(wrapper.pi, 2)) -- 3.14