Metatable Tutorial

A basic overview of how metatables work.

by little5

Author Avatar

Overview:

This tutorial will go over the basic concepts of metatables and their uses.

Prerequisites:

What are metatables?

A metatable, at it's most basic definition, is a table with a metamethod attached to it. A metamethod is a sort of event trigger that calls a function when a certain operation is performed on the metatable, hence the name "meta".

Meta: Pertaining to a thing's self (in this instance, a table) Table: A container that stores values

In the first example we will create a metatable with the __index metamethod attached. The __index metamethod is called when you index a nil value in a table, and can return something in it's stead.

To create a new metatable, you use the setmetatable() function:

local t = {}

function setDefault(t, d) -- from Programming in Lua, 13.4.3 - Tables with Default Values
    local mt = {__index = function() return d end}
    setmetatable(t, mt)
end

local values = {
    foo = "foo";
    bar = "bar";
}

print(values.blah) -- nil

setDefault(values, "blah")

print(values.blah) -- blah

This code defines a function that, when a table and a value is passed in, sets any nil value in that table to return what was passed in. We can see this in line 12 in that when we print values.blah, it prints nil. However, when we call setDefault and pass in values and "blah", any time you index a nil value, the metamethod will return "blah".

Another useful metamethod is the __newindex metamethod. Like the __index metamethod, it's fired when you try to assign a value that was originally nil.

function makeReadOnly(t)
    local mt = {__newindex = function() return error("Table is read-only.") end}
    setmetatable(t, mt)
end

local t = {}
t.foo = "foo"
makeReadOnly(t)
t.bar = "bar" -- will error: "Table is read-only."

There are many other metamethods we can use, but in practice, most of them aren't particularly useful on Roblox. These include __add, __sub, __mul (multiplication), __div (division), __unm (negation), and __pow (exponentation). Here's an example of how __add is used:

local mt = {
    __add = function(t0, t1)
        local rt = {}
        for i = 1, math.min(#t0, #t1) do
            table.insert(rt, (t0[i] + t1[i]))
        end
        return rt -- returns a table with the sums of each of the elements of the original tables
    end
}

local t0 = setmetatable({1, 2, 3}, mt)
local t1 = setmetatable({3, 2, 1}, mt)
local t2 = t0 + t1

for i, v in ipairs(t2) do 
    print(v) -- 4 x3
end

The rest of them follow the same principle: the function takes in two tables and can return a value.

Summary:

In this tutorial, you learned how to use the __index metamethod, the __newindex metamethod, and the arithmetic metamethods. With these, you can make default values for tables, make read-only tables, and make it so that you can add two tables together to get their element's sums.

At their core, metatables are like event handlers; they take in events such as indexing a nil value, run the context through a function, and return a result.

View in-game to comment, award, and more!