 This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.

## 13.1 – Arithmetic Metamethods

In this section, we will introduce a simple example to explain how to use metatables. Suppose we are using tables to represent sets, with functions to compute the union of two sets, intersection, and the like. As we did with lists, we store these functions inside a table and we define a constructor to create new sets:

```    Set = {}

function Set.new (t)
local set = {}
for _, l in ipairs(t) do set[l] = true end
return set
end

function Set.union (a,b)
local res = Set.new{}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end

function Set.intersection (a,b)
local res = Set.new{}
for k in pairs(a) do
res[k] = b[k]
end
return res
end
```
To help checking our examples, we also define a function to print sets:
```    function Set.tostring (set)
local s = "{"
local sep = ""
for e in pairs(set) do
s = s .. sep .. e
sep = ", "
end
return s .. "}"
end

function Set.print (s)
print(Set.tostring(s))
end
```

Now, we want to make the addition operator (``+`´) compute the union of two sets. For that, we will arrange that all tables representing sets share a metatable and this metatable will define how they react to the addition operator. Our first step is to create a regular table that we will use as the metatable for sets. To avoid polluting our namespace, we will store it in the `Set` table:

```    Set.mt = {}    -- metatable for sets
```
The next step is to modify the `Set.new` function, which creates sets. The new version has only one extra line, which sets `mt` as the metatable for the tables that it creates:
```    function Set.new (t)   -- 2nd version
local set = {}
setmetatable(set, Set.mt)
for _, l in ipairs(t) do set[l] = true end
return set
end
```
After that, every set we create with `Set.new` will have that same table as its metatable:
```    s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
print(getmetatable(s1))          --> table: 00672B60
print(getmetatable(s2))          --> table: 00672B60
```

Finally, we add to the metatable the so-called metamethod, a field `__add` that describes how to perform the union:

```    Set.mt.__add = Set.union
```
Whenever Lua tries to add two sets, it will call this function, with the two operands as arguments.

With the metamethod in place, we can use the addition operator to do set unions:

```    s3 = s1 + s2
Set.print(s3)  --> {1, 10, 20, 30, 50}
```
Similarly, we may use the multiplication operator to perform set intersection:
```    Set.mt.__mul = Set.intersection

Set.print((s1 + s2)*s1)     --> {10, 20, 30, 50}
```

For each arithmetic operator there is a corresponding field name in a metatable. Besides `__add` and `__mul`, there are `__sub` (for subtraction), `__div` (for division), `__unm` (for negation), and `__pow` (for exponentiation). We may define also the field `__concat`, to define a behavior for the concatenation operator.

When we add two sets, there is no question about what metatable to use. However, we may write an expression that mixes two values with different metatables, for instance like this:

```    s = Set.new{1,2,3}
s = s + 8
```
To choose a metamethod, Lua does the following: (1) If the first value has a metatable with an `__add` field, Lua uses this value as the metamethod, independently of the second value; (2) otherwise, if the second value has a metatable with an `__add` field, Lua uses this value as the metamethod; (3) otherwise, Lua raises an error. Therefore, the last example will call `Set.union`, as will the expressions `10 + s` and `"hy" + s`.

Lua does not care about those mixed types, but our implementation does. If we run the `s = s + 8` example, the error we get will be inside `Set.union`:

```    bad argument #1 to `pairs' (table expected, got number)
```
If we want more lucid error messages, we must check the type of the operands explicitly before attempting to perform the operation:
```    function Set.union (a,b)
if getmetatable(a) ~= Set.mt or
getmetatable(b) ~= Set.mt then
error("attempt to `add' a set with a non-set value", 2)
end
...  -- same as before
```