METATABLES


In Lua, metatables are tables that can define custom behavior for other tables. Every table in Lua can have a metatable, which is a separate table that is associated with the original table. When an operation is performed on a table and Lua cannot find the operation in the table itself, it will check the metatable to see if it has a function for handling the operation.

For example, let's say you have a table named tbl. You can create a metatable, named mtbl, for this table like this:

tbl = {}
mtbl = {}

setmetatable( tbl, mtbl )

Now any operation performed on tbl that Lua cannot find will check mtbl instead. This allows you to define custom behavior for certain operations.



One common use for metatables is to define operators for tables. For example, if you try to add two tables together ( tbl1+tbl2 ), you will get an error because this is not a default operation, but with metatables you can define what should happen when the + operator is used on your table like this:

mtbl = {
	--__add called when + operator is used
	__add = function(a, b) 
		return { x = (a.x+b.x), y = (a.y+b.y) }
	end
}

--create tables and set metatables
tbl1 = {x=1, y=2}
tbl2 = {x=4, y=5}
setmetatable(tbl1, mtbl)
setmetatable(tbl2, mtbl)

--add tables uses metatable defined function for +
local tbl3 = tbl1 + tbl2 

-- tbl3 is now {x=5, y=7}

In this example, we create a metatable mtbl that defines the __add function, which is called when the + operator is used on tables. The __add function returns a new table that is populated with the sum of the x and y elements of the two tables being added: a and b.



Another use for metatables is to add default values to tables. For example, if your table does not have a key defined, but your code tries to use it, the value of the key will be nil, and often error if trying to perform an operation on nil. So instead, you can define a metatable that returns a default value of 0 for any key that does not exist in the table:

tbl = {}
tbl.foo = 42

print(tbl.foo) -- prints 42
print(tbl.bar) -- prints [nil]
print(tbl.foo+tbl.bar) -- error!

mtbl = {
	__index = function(table, key)
		return 0
	end
}
setmetatable(tbl, mtbl)

print(tbl.bar) -- prints 0
print(tbl.foo+tbl.bar) --prints 42, no error

In this example, we create a metatable mtblthat defines the __index function, which is a function called when Lua tries to access a key in tbl that does not exist. The __index function simply returns 0 in this case, providing a default value for any non-existent keys.

Overall, metatables are a powerful feature of Lua that allow you to customize the behavior of tables in many ways. However, they can also be a bit tricky to use, so it's important to read the Lua documentation carefully and practice using them to gain a deeper understanding.

Important note from Lua Documentation on Metatables:

"Any table can be the metatable of any other table; a group of related tables may share a common metatable (which describes their common behavior); a table can be its own metatable (so that it describes its own individual behavior). Any configuration is valid."


To remove a metatable from a table, set the metatable to nil:

setmetatable( tbl, nil )



Here are the operations controlled by metatables from the Lua Manual:

__add the addition (+) operation. If any operand for an addition is not a number, Lua will try to call a metamethod. It starts by checking the first operand (even if it is a number); if that operand does not define a metamethod for __add, then Lua will check the second operand. If Lua can find a metamethod, it calls the metamethod with the two operands as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, if no metamethod is found, Lua raises an error.
__sub the subtraction (-) operation. Behavior similar to the addition operation.
__mul the multiplication (*) operation. Behavior similar to the addition operation.
__div the division (/) operation. Behavior similar to the addition operation.
__mod the modulo (%) operation. Behavior similar to the addition operation.
__pow the exponentiation (^) operation. Behavior similar to the addition operation.
__unm the negation (unary -) operation. Behavior similar to the addition operation.
__idiv the floor division (//) operation. Behavior similar to the addition operation.
__band the bitwise AND (&) operation. Behavior similar to the addition operation, except that Lua will try a metamethod if any operand is neither an integer nor a float coercible to an integer.
__bor the bitwise OR (|) operation. Behavior similar to the bitwise AND operation.
__bxor the bitwise exclusive OR (binary ~) operation. Behavior similar to the bitwise AND operation.
__bnot the bitwise NOT (unary ~) operation. Behavior similar to the bitwise AND operation.
__shl the bitwise left shift (<<) operation. Behavior similar to the bitwise AND operation.
__shr the bitwise right shift (>>) operation. Behavior similar to the bitwise AND operation.
__concat the concatenation (..) operation. Behavior similar to the addition operation, except that Lua will try a metamethod if any operand is neither a string nor a number (which is always coercible to a string).
__len explathe length (#) operation. If the object is not a string, Lua will try its metamethod. If there is a metamethod, Lua calls it with the object as argument, and the result of the call (always adjusted to one value) is the result of the operation. If there is no metamethod but the object is a table, then Lua uses the table length operation (see §3.4.7). Otherwise, Lua raises an error.ined
__eq the equal (==) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are either both tables or both full userdata and they are not primitively equal. The result of the call is always converted to a boolean.
__lt the less than (<) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are neither both numbers nor both strings. Moreover, the result of the call is always converted to a boolean.
__le the less equal (<=) operation. Behavior similar to the less than operation.
__index The indexing access operation table[key]. This event happens when table is not a table or when key is not present in table. The metavalue is looked up in the metatable of table. The metavalue for this event can be either a function, a table, or any value with an __index metavalue. If it is a function, it is called with table and key as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, the final result is the result of indexing this metavalue with key. This indexing is regular, not raw, and therefore can trigger another __index metavalue.
__newindex The indexing assignment table[key] = value. Like the index event, this event happens when table is not a table or when key is not present in table. The metavalue is looked up in the metatable of table. Like with indexing, the metavalue for this event can be either a function, a table, or any value with an __newindex metavalue. If it is a function, it is called with table, key, and value as arguments. Otherwise, Lua repeats the indexing assignment over this metavalue with the same key and value. This assignment is regular, not raw, and therefore can trigger another__newindex metavalue. Whenever a __newindex metavalue is invoked, Lua does not perform the primitive assignment. If needed, the metamethod itself can call rawset to do the assignment.
__call The call operation func(args). This event happens when Lua tries to call a non-function value (that is, func is not a function). The metamethod is looked up in func. If present, the metamethod is called with func as its first argument, followed by the arguments of the original call (args). All results of the call are the results of the operation. This is the only metamethod that allows multiple results.

748

3 Nov 2023

Font