metatables
metatables
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.
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
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