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!
There are a few pieces of confusion in this question.
x
a metatable?You ask "Is x
a metatable?" However, this isn't really a meaningful question.
Any table may be used as a metatable of another table.
Each and every table has the opportunity to point to one other table and declare that other table to be its metatable.
Any table may the metatable of any metatable. Multiple tables can have the same metatable. A metatable of a table may in turn have its own metatable. A table may be its own metatable.
You can get x
's metatable using getmetatable(x)
.
You can change x
's metatable to become m
by calling setmetatable(x, m)
. setmetatable
does not inspect either of its arguments (more than verifying that they are both tables). It just sets the pointer of x
, so that subsequently m
is x
's metatable.
Some versions of your question that are more answerable:
m
the metatable of x
?x
the metatable of some table?
m
be useful as a metatable? == Does it assign handlers for any metamethods?
m
may in turn have a metatable with a __index
metamethod that produces metamethods for m
!The call setmetatable
does very little. It only modifies the invisible metatable
field on the given table.
Whenever Lua is asked to perform an operation on a table, it first tries to resolve it on its own.
For indexing t[key]
,
t
associates a value with key
. If it does, it produces that value.t
has a metatable set. If it does, it accesses getmetatable(t).__index
. If this is nil
, it produces nil
.__index
table/function to decide what to return.Lua performs these steps each and every time you access any table. Thus, if you modify either t
or t
's metatable link or the table that t
has set its metatable to, subsequent accesses will use those modifications.
Roughly, this is how indexing a table works in Lua:
function index(t, key) local v = rawget(t, key) if not rawequal(v, nil) then return v end local m = getmetatable(t) if not m then return nil end local metamethod = m.__index if rawequal(metamethod, nil) then return nil elseif type(metamethod) == "table" then return index(metamethod, key) elseif type(metamethod) == "function" then return metamethod(t, key) end error("bad __index type `" .. type(metamethod) .. "`") end
Code more-or-less equivalent to the above runs for each and every []
and .
and :
in your program.
Locked by User#19524
This question has been locked to preserve its current state and prevent spam and unwanted comments and answers.
Why was this question closed?