Today I was having an interesting conversation/debate with a member of the discord. The topic was where metatables are actually created. The other person in this conversation argued that a metatable is created when the table is created like so:
local meta = {
__index = function() return "nothing here to see" end
} -- he debated that this was a metatable already
local t = {}
setmetatable(t, meta) -- and I debated that if this line of code never happens meta is not a metatable
After discussing for a while, he clarified that it was because of the metamethods that he thought it was a metatable so I did this:
local meta = {
"hello"
} -- he debated that this was a metatable already
local t = {}
setmetatable(t, meta) -- and I debated that if this line of code never happens meta is not a metatable
and asked if meta is still a metatable before setmetatable. He argued that because "setmetatable(tbl, mt) does not change the behavior of 'mt' at all..." mt is already a metatable. He then asked with the following code:
local mt = {
__index = function() return "index not found" end
}
setmetatable({}, mt)
"^metatable?? which argument is setmetatable affecting, the first one or the second?" I then replied with this code:
local mt = {
__index = function() return "index not found" end
}
-- not metatable yet
setmetatable({}, mt)
--metatable now
After reading, he asked, "So you're saying that a table with metamethods needs to actively be attached to a table for it to be considered a metatable?" I replied with yes. He then asked, "Can you show me what makes you think that? because i am not opposed to saying "you're right" but i do not think that you are right. setmetatable({}, mt) which argument does this affect? {} or mt?" I replied with, "the first argument is the table that the metatable is attatched to" and "a metatable is a table that is by definition the metatable of another table so you cannot have a metatable unless it is connected to a table and what a metatable contains certainly does not affect that it is or is not a metatable." and he said, "but right now youre just acting like i should believe you where does this imply that {} is not a metatable but setmetatable({}, {}) is" so I said, "are you telling me that without ever using setmetatable, a table I call metatable is a metatable? because that is wrong. It is just a table that is my point" In response he wrote this code and statement:
local x = {
__index = function() return "not found" end
}
setmetatable({}, x}[1] -> "not found"
"Nothing inherently changes in the x table, but the {} now has instructions for what to do (in this specific case) if a nil value is indexed." So I provided this code in response:
local a = {}
local b = {}
setmetatable(a, b)
would work and
local a = {}
local b = {}
setmetatable(b, a)
-- would work
commenting that "so neither a nor b is a metatable at the beginning no matter what is in them they are only a metatable if I use setmetatable" He then provided some code that really sparked my curiousity:
local meta = {
__index = function(self, index) return index + 5 end
}
local t = {}
setmetatable(t, meta)
meta = nil
print(t[3]) -- this printed 8
After extensive testing someone reminded me that meta = nil would not change the elements in the table. So, I wrote this code instead:
local meta = {
"hello"
}
local t = {}
setmetatable(t, meta)
for i,v in pairs(meta) do
print(type(v))
meta[i] = nil
end
print(#meta)
print(getmetatable(t)[1]) -- it worked and printed nil
At this point I was pretty confused about the whole deal. Was I wrong and he right? Was I right and he wrong? Were we both wrong? After discussing further he tested this code:
local meta = {
__index = {[3] = "hi"}
}
local t = {}
setmetatable(t, meta)
for i,v in pairs(meta) do
meta[i] = nil
end
print(setmetatable({}, getmetatable(t))[3]) -- printed nil
and he said, "this prints nil so that means im wrong on the fact that it doesnt run off of the original metatable" which confuses me. How does that prove he was wrong? After even further discussion we decided to ask a question so that the experts could answer it. He said that "the basis of the question should be does doing setmetatable(arg1, arg2) inherently make arg2 a metatable, or does it remain an ordinary table? (assume that arg1 and arg2 are both tables)." Anyway, I hope you, the reader, will be able to understand and help. I would really appreciate a decent explanation of how metatables actually function. Thanks!

9 comments
this is a great question. upvote this, people
oh crap, I actually wanted to ask this question like a year ago but thought it would get downvoted LOL
the heck 8 upvotes
Good question.
Thanks :D
I'm in support with incapaz; this question needs more upvotes. This's a gem.
" "this prints nil so that means im wrong on the fact that it doesnt run off of the original metatable" which confuses me. How does that prove he was wrong?" I said I was wrong because I had previously (incorrectly) stated that when you use setmetatable(arg1, arg2), it creates new functions for arg1 based off of arg2, rather than controlling it all from the metatable.
I found an article at roblox wiki that may help: http://wiki.roblox.com/index.php?title=User:Anaminus/metatables_nutshell
Sorry for any unclear wording or phases or even missing paragraphs. I was trying to concicely represent everything that went on in the discussion and obviously was unable to do that perfectly.