Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
15

How do metatables function and how are they actually created? [closed]

Asked by 5 years ago
Edited 5 years ago

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!

6
this is a great question. upvote this, people User#19524 175 — 5y
0
oh crap, I actually wanted to ask this question like a year ago but thought it would get downvoted LOL greatneil80 2647 — 5y
0
the heck 8 upvotes User#19524 175 — 5y
0
Good question. TheeDeathCaster 2368 — 5y
View all comments (5 more)
0
Thanks :D User#21908 42 — 5y
0
I'm in support with incapaz; this question needs more upvotes. This's a gem. TheeDeathCaster 2368 — 5y
0
" "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. zaniac10 15 — 5y
0
I found an article at roblox wiki that may help: http://wiki.roblox.com/index.php?title=User:Anaminus/metatables_nutshell cailir 284 — 5y
0
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. User#21908 42 — 5y

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?

1 answer

Log in to vote
7
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
5 years ago
Edited 5 years ago

There are a few pieces of confusion in this question.

"Is 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:

  • Is m the metatable of x?
  • Is x the metatable of some table?
    • A "yes/no" answer is not always easy, because that table may no longer be reachable (or only be reachable from weak references!)
  • Might m be useful as a metatable? == Does it assign handlers for any metamethods?
    • This is not answerable in general because m may in turn have a metatable with a __index metamethod that produces metamethods for m!

What metatables do

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],

  • Lua checks if t associates a value with key. If it does, it produces that value.
  • Otherwise, it checks if t has a metatable set. If it does, it accesses getmetatable(t).__index. If this is nil, it produces nil.
  • Otherwise, it consults the __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.

0
Sorry some of the phrasings in my question were not clear. I was trying to concisely represent what went on in our conversation and may have missed some elements. I am also only sixteen so my ability to write well is limited at best. User#21908 42 — 5y
0
This is a great answer! Thank you so much! User#21908 42 — 5y
4
Blue taslem is back!! User#19524 175 — 5y
Ad