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.4.3 – Tables with Default Values

The default value of any field in a regular table is nil. It is easy to change this default value with metatables:

    function setDefault (t, d)
      local mt = {__index = function () return d end}
      setmetatable(t, mt)
    end
    
    tab = {x=10, y=20}
    print(tab.x, tab.z)     --> 10   nil
    setDefault(tab, 0)
    print(tab.x, tab.z)     --> 10   0
Now, whenever we access an absent field in tab, its __index metamethod is called and returns zero, which is the value of d for that metamethod.

The setDefault function creates a new metatable for each table that needs a default value. This may be expensive if we have many tables that need default values. However, the metatable has the default value d wired into itself, so the function cannot use a single metatable for all tables. To allow the use of a single metatable for tables with different default values, we can store the default value of each table in the table itself, using an exclusive field. If we are not worried about name clashes, we can use a key like "___" for our exclusive field:

    local mt = {__index = function (t) return t.___ end}
    function setDefault (t, d)
      t.___ = d
      setmetatable(t, mt)
    end
If we are worried about name clashes, it is easy to ensure the uniqueness of this special key. All we need is to create a new table and use it as the key:
    local key = {}    -- unique key
    local mt = {__index = function (t) return t[key] end}
    function setDefault (t, d)
      t[key] = d
      setmetatable(t, mt)
    end

An alternative approach to associating each table with its default value is to use a separate table, where the indices are the tables and the values are their default values. However, for the correct implementation of this approach we need a special breed of table, called weak tables, and so we will not use it here; we will return to the subject in Chapter 17.

Another alternative is to memoize metatables in order to reuse the same metatable for tables with the same default. However, that needs weak tables too, so that again we will have to wait until Chapter 17.