metatables
events

To every table it is possible to associate another table, its metatable. Thus

  setmetatable (t, m)  
sets a table  m to be the metatable of a table   t . If m is nil then t's metatable is removed. The function returns its first argument. If   t's metatable has the key __metatable the function raises an error. This gives a means of locking a table's metatable so that it cannot be changed.

The expression  getmetatable(t) yields nil if t has no metatable. If t's metatable has a key  "__metatable" then it yields the corresponding value; otherwise the expression yields   t's metatable.

Metatables can be used to modify Lua's syntax. This is done by assigning values to special keys, known as events, in metatables. By convention these events are certain strings whose first two characters are underscores. The  "__metatable" event prevents the updating of metatables.

reading nonexistent keys

The __index event controls what happens when an attempt is made to look up a value in a table, say t, at a nonexistent key, say k. If the table has no metatable the default action is to return nil. If it does have a metatable, say  m and m.__index is a table, say q then the expression t[k] is taken to be q[k]. If m.__index is a function f then t[k] is taken to be f(t,k).

This enables tables to inherit all the keys of a parent table, and to override them if necessary.

  child = function (parent)
        return setmetatable ({ }, { __index = parent }) end
  circle = { x = 0, y = 0, r = 10 }
  blob = child (circle)
  blob.colour = 0x00ff00 -- new key
  blob.r = 20            -- override r key
updating nonexistent keys

The __newindex event controls what happens when an attempt is made to write a value, say v to a table, say t, at a nonexistent key, say k. If the table has no metatable, and v is not nil, then the table acquires a new key k with value v. If it does have a metatable, say m and m.__newindex is a table, say q then the statement q[k] = v is executed. If m.__newindex is a function, say f, then the statement f(t,k,v) is executed.

By way of an example we can implement tables that are case-insensitive as far as keys are concerned, like this:

caseless = function ( )
         return setmetatable ({ }, {
           __index = function (t, k)
                     if type (k) == "string" then
                       k = k:lower ( )
                     end
                     return rawget (t, k) end,
           __newindex = function (t, k, v)
                     if type (k) == "string" then
                       k = k:lower ( )
                     end
                     return rawset (t, k, v) end }) end
w = caseless ( )
w.ABC = 37
print (w.abc) --> 37