Understanding Lua Metatables

The Lua programming language does not have a class-based object oriented system in its definition. Lua metatables are the most common way of implementing such a system, and is analygous in usefulness to method_missing in Ruby or __getattr__ in Python.

Lua Tables

A table in Lua is a way to associate values with keys of values. By allowing generic association, the table can be used both as an array of values indexed by number, or a structure more resembling HashMaps in other languages, where values are indexed by string, or by another value type.

There are several ways to instantiate a table:

1
t={5, 3, y="blah", 4, x="stuff"}

Or:

1
2
t={}
t.x="stuff"

Or:

1
2
3
t={}
t["x"]="stuff"
t[1]=123

In the first case, all unkeyed values are inserted into number indexes by default in the order they appear. The keyed values are indexed by key and not by number.

Table Methods

To create a function value in a table, you can use the following syntax:

1
2
3
t={}
t.foo = function (x) return x*x end
t.foo(9) --returns 81

Or to make it a method that passes self:

1
2
3
4
5
t={}
t.x=5
t.foo = function (self, y) return self.x*y end
t.foo(t, 4) --returns 20
t:foo(4) --equivalent, also returns 20

Note that functions in Lua are values that can be assigned to variables.

Metatables, Finally

Let’s start with some code to think through (pulled from https://www.lua.org/pil/13.html):

1
2
3
4
5
t={}
getmetatable(t) --returns nil
t1={}
setmetatable(t, t1)
assert(getmetatable(t) == t1)

So any table can be set as the metatable of another table. The metatable describes the behavior of a table by exposing what members of the table are available to access. By adding __add, __mul, __sub, __div, __unm, __pow, __concat, __lt, __le

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Vec = {}
Vec.mt = {}
Vec.new = function (x, y, z)
local v = {x, y, z}
setmetatable(v, Vec.mt)
return v
end
function Vec.dot(a, b)
local v = Vec.new(a[1]*b[1], a[2]*b[2], a[3]*b[3])
return v
end
Vec.mt.__mul = Vec.dot
v1=Vec.new(1, 2, 3)
v2=Vec.new(2, 3, 4)
v3=Vec.dot(v1, v2) --returns a table {2, 6, 12}
v4=v1*v2 --v1*v2 --equivalent, returns a table {2, 6, 12}
```lua
For behavior similar to method_missing in Ruby or to \_\_getattr\_\_ in Python, use a \_\_index method (or table) in the metatable that will trigger when there is no member found. The \_\_index method will take the following form:
```lua
Vec.mt.__index = function(table, key)
--Do something to get the desire value given the key here
end
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×