Thanks! This is an amazing example; here is my line by line take on what is happening:
- line 3 - allocates memory for an empty table and sets the global a to reference that empty table
- line 4 - sets the string key 'a' of global a to the global a, which sets the key 'a' of the empty table to reference itself
- line 5 - prints the memory address of the empty table because the global a points to the empty table which looks up the key 'a' which is a reference to itself. This lookup process repeats until the chain finally reaches the last key 'a' which references the empty table
- line 7 - sets the global a to nil and the global b to reference the empty table
- line 8 - tries to print the global a which is set to nil causing an nil index error
- line 13 - prints the memory address of the empty table because the global b references the empty table. The same chain of index lookups for the key 'a' repeats until it reaches the final key 'a' which references the empty table
- line 15 - tries to follow the chain of index lookups for the key 'a' like in line 13 until the final key 'b' which being set to nil triggers the index of the global b which does indeed exist.
This is where I had to stop and do a little homework. After checking the
Lua 5.2 Reference Manual I found this important part to the index metamethod:
- Code: Select all
function gettable_event (table, key)
local h
if type(table) == "table" then
local v = rawget(table, key)
-- if key is present, return raw value
if v ~= nil then return v end
h = metatable(table).__index
if h == nil then return nil end
else
h = metatable(table).__index
if h == nil then
error(···)
end
end
if type(h) == "function" then
return (h(table, key)) -- call the handler
else return h[key] -- or repeat operation on it
end
end
The import part is line 3 which checks if the type is a table. If so, then it will do a rawget to see if there is actually a value present and return that value or nil. This is the reason that we get a nil on the final key '
b', because we are trying to index a table and coming up empty which is returning a nil. Since the global
b does exist as a reference to a table type, we are able to index a key to determine if it exists - hence the nil. Since nil is a value that can be printed, it successfully prints.
- line 17 - tries to follow the chain until the first key 'b' is reached. At this point, because the key 'b' is set to nil (which we know from our previous print in line 15), so we can no longer continue on our chain to the next key 'a'. This causes a nil index error for trying to access the key 'b'.
- line 22 - set the global b to nil. This means that the only reference that exists to the empty table is the key 'a' which is a reference to itself so it is garbage collected
You have successfully explained
ephemeron tables through example instead of the abstract and technical definition in the Lua 5.2 Reference Manual. After reading through the manual, that was one of the concepts that was still a bit fuzzy.
Our empty table is an
ephemeron table starting at
line 7 because at that point it contains weak keys and strong values. This is due to the fact that the actual empty table is the strong value and it is still reachable through the global
b. The key '
a' is however weak because it is simply a reference to itself - only having any context while it is still reachable through the global
b.
Once the global
b is set to nil on
line 22, the only reference for the key '
a' comes through its value (the empty table), so the key/value pair is set for garbage collection. Since that is the only key/value pair in our table and nothing references the table it is garbage collected

Again, thanks very much for this great example that teaches through direct experience!! That is how I process new concepts and make them stick. I'm sure others will find this example very useful.