2.4 – Metatables and Metamethods

Every value in Lua can have a metatable. This metatable is an ordinary Lua table that defines the behavior of the original value under certain special operations. You can change several aspects of the behavior of operations over a value by setting specific fields in its metatable. For instance, when a non-numeric value is the operand of an addition, Lua checks for a function in the field “__add“ of the value’s metatable. If it finds one, Lua calls this function to perform the addition.

The keys in a metatable are derived from the event names; the corresponding values are called metamethods. In the previous example, the event is "add" and the metamethod is the function that performs the addition.

You can query the metatable of any value using the getmetatable function.

You can replace the metatable of tables using the setmetatable function. You cannot change the metatable of other types from Lua (except by using the debug library); you must use the C API for that.

Tables and full userdata have individual metatables (although multiple tables and userdata can share their metatables). Values of all other types share one single metatable per type; that is, there is one single metatable for all numbers, one for all strings, etc. By default, a value has no metatable, but the string library sets a metatable for the string type (see §6.4).

A metatable controls how an object behaves in arithmetic operations, order comparisons, concatenation, length operation, and indexing. A metatable also can define a function to be called when a userdata or a table is garbage collected. When Lua performs one of these operations over a value, it checks whether this value has a metatable with the corresponding event. If so, the value associated with that key (the metamethod) controls how Lua will perform the operation.

Metatables control the operations listed next. Each operation is identified by its corresponding name. The key for each operation is a string with its name prefixed by two underscores, ‘__‘; for instance, the key for operation “add” is the string “__add“.

The semantics of these operations is better explained by a Lua function describing how the interpreter executes the operation. The code shown here in Lua is only illustrative; the real behavior is hard coded in the interpreter and it is much more efficient than this simulation. All functions used in these descriptions (rawget, tonumber, etc.) are described in §6.1. In particular, to retrieve the metamethod of a given object, we use the expression

  1. metatable(obj)[event]

This should be read as

  1. rawget(getmetatable(obj) or {}, event)

This means that the access to a metamethod does not invoke other metamethods, and access to objects with no metatables does not fail (it simply results in nil).

For the unary - and # operators, the metamethod is called with a dummy second argument. This extra argument is only to simplify Lua’s internals; it may be removed in future versions and therefore it is not present in the following code. (For most uses this extra argument is irrelevant.)

  • “add”: the + operation.

    The function getbinhandler below defines how Lua chooses a handler for a binary operation. First, Lua tries the first operand. If its type does not define a handler for the operation, then Lua tries the second operand.

    1. function getbinhandler (op1, op2, event)
    2. return metatable(op1)[event] or metatable(op2)[event]
    3. end

    By using this function, the behavior of the op1 + op2 is

    1. function add_event (op1, op2)
    2. local o1, o2 = tonumber(op1), tonumber(op2)
    3. if o1 and o2 then -- both operands are numeric?
    4. return o1 + o2 -- '+' here is the primitive 'add'
    5. else -- at least one of the operands is not numeric
    6. local h = getbinhandler(op1, op2, "__add")
    7. if h then
    8. -- call the handler with both operands
    9. return (h(op1, op2))
    10. else -- no handler available: default behavior
    11. error(···)
    12. end
    13. end
    14. end
  • “sub”: the - operation. Behavior similar to the “add” operation.

  • “mul”: the * operation. Behavior similar to the “add” operation.
  • “div”: the / operation. Behavior similar to the “add” operation.
  • “mod”: the % operation. Behavior similar to the “add” operation, with the operation o1 - floor(o1/o2)*o2 as the primitive operation.
  • “pow”: the ^ (exponentiation) operation. Behavior similar to the “add” operation, with the function pow (from the C math library) as the primitive operation.
  • “unm”: the unary - operation.

    1. function unm_event (op)
    2. local o = tonumber(op)
    3. if o then -- operand is numeric?
    4. return -o -- '-' here is the primitive 'unm'
    5. else -- the operand is not numeric.
    6. -- Try to get a handler from the operand
    7. local h = metatable(op).__unm
    8. if h then
    9. -- call the handler with the operand
    10. return (h(op))
    11. else -- no handler available: default behavior
    12. error(···)
    13. end
    14. end
    15. end
  • “concat”: the .. (concatenation) operation.

    1. function concat_event (op1, op2)
    2. if (type(op1) == "string" or type(op1) == "number") and
    3. (type(op2) == "string" or type(op2) == "number") then
    4. return op1 .. op2 -- primitive string concatenation
    5. else
    6. local h = getbinhandler(op1, op2, "__concat")
    7. if h then
    8. return (h(op1, op2))
    9. else
    10. error(···)
    11. end
    12. end
    13. end
  • “len”: the # operation.

    1. function len_event (op)
    2. if type(op) == "string" then
    3. return strlen(op) -- primitive string length
    4. else
    5. local h = metatable(op).__len
    6. if h then
    7. return (h(op)) -- call handler with the operand
    8. elseif type(op) == "table" then
    9. return #op -- primitive table length
    10. else -- no handler available: error
    11. error(···)
    12. end
    13. end
    14. end

    See §3.4.6 for a description of the length of a table.

  • “eq”: the == operation. The function getequalhandler defines how Lua chooses a metamethod for equality. A metamethod is selected only when both values being compared have the same type and the same metamethod for the selected operation, and the values are either tables or full userdata.

    1. function getequalhandler (op1, op2)
    2. if type(op1) ~= type(op2) or
    3. (type(op1) ~= "table" and type(op1) ~= "userdata") then
    4. return nil -- different values
    5. end
    6. local mm1 = metatable(op1).__eq
    7. local mm2 = metatable(op2).__eq
    8. if mm1 == mm2 then return mm1 else return nil end
    9. end

    The “eq” event is defined as follows:

    1. function eq_event (op1, op2)
    2. if op1 == op2 then -- primitive equal?
    3. return true -- values are equal
    4. end
    5. -- try metamethod
    6. local h = getequalhandler(op1, op2)
    7. if h then
    8. return not not h(op1, op2)
    9. else
    10. return false
    11. end
    12. end

    Note that the result is always a boolean.

  • “lt”: the < operation.

    1. function lt_event (op1, op2)
    2. if type(op1) == "number" and type(op2) == "number" then
    3. return op1 < op2 -- numeric comparison
    4. elseif type(op1) == "string" and type(op2) == "string" then
    5. return op1 < op2 -- lexicographic comparison
    6. else
    7. local h = getbinhandler(op1, op2, "__lt")
    8. if h then
    9. return not not h(op1, op2)
    10. else
    11. error(···)
    12. end
    13. end
    14. end

    Note that the result is always a boolean.

  • “le”: the <= operation.

    1. function le_event (op1, op2)
    2. if type(op1) == "number" and type(op2) == "number" then
    3. return op1 <= op2 -- numeric comparison
    4. elseif type(op1) == "string" and type(op2) == "string" then
    5. return op1 <= op2 -- lexicographic comparison
    6. else
    7. local h = getbinhandler(op1, op2, "__le")
    8. if h then
    9. return not not h(op1, op2)
    10. else
    11. h = getbinhandler(op1, op2, "__lt")
    12. if h then
    13. return not h(op2, op1)
    14. else
    15. error(···)
    16. end
    17. end
    18. end
    19. end

    Note that, in the absence of a “le” metamethod, Lua tries the “lt”, assuming that a <= b is equivalent to not (b < a).

    As with the other comparison operators, the result is always a boolean.

  • “index”: The indexing access table[key]. Note that the metamethod is tried only when key is not present in table. (When table is not a table, no key is ever present, so the metamethod is always tried.)

    1. function gettable_event (table, key)
    2. local h
    3. if type(table) == "table" then
    4. local v = rawget(table, key)
    5. -- if key is present, return raw value
    6. if v ~= nil then return v end
    7. h = metatable(table).__index
    8. if h == nil then return nil end
    9. else
    10. h = metatable(table).__index
    11. if h == nil then
    12. error(···)
    13. end
    14. end
    15. if type(h) == "function" then
    16. return (h(table, key)) -- call the handler
    17. else return h[key] -- or repeat operation on it
    18. end
    19. end
  • “newindex”: The indexing assignment table[key] = value. Note that the metamethod is tried only when key is not present in table.

    1. function settable_event (table, key, value)
    2. local h
    3. if type(table) == "table" then
    4. local v = rawget(table, key)
    5. -- if key is present, do raw assignment
    6. if v ~= nil then rawset(table, key, value); return end
    7. h = metatable(table).__newindex
    8. if h == nil then rawset(table, key, value); return end
    9. else
    10. h = metatable(table).__newindex
    11. if h == nil then
    12. error(···)
    13. end
    14. end
    15. if type(h) == "function" then
    16. h(table, key,value) -- call the handler
    17. else h[key] = value -- or repeat operation on it
    18. end
    19. end
  • “call”: called when Lua calls a value.

    1. function function_event (func, ...)
    2. if type(func) == "function" then
    3. return func(...) -- primitive call
    4. else
    5. local h = metatable(func).__call
    6. if h then
    7. return h(func, ...)
    8. else
    9. error(···)
    10. end
    11. end
    12. end