Hi everyone,
I'm having quite a bit of trouble understanding how this piece of code works. I understand that there is comments to help understand what the scripter is doing but frankly i cant seem to make sense of it. The code above was created by scriptguider. The link to the original post can be found here: https://scriptinghelpers.org/questions/39391/wrapping-instances-with-robloxs-lua
In particular, i need help understanding:
Why interface and behaviour were specifically chosen as the name of the variable in the function.
How the local part interacts with the part in the WrapPart function.
What behavior.__index = part does
What function behavior:__newindex(key, value) ....... end does
How return setmetatable(interface, behavior) interacts with the rest of the code.
-- Create part first local part = Instance.new("Part", workspace) local function WrapPart(part) local interface = {} local behavior = {} -- Object interface -- interface.BirthTime = os.time() -- Create property -- Interface behavior -- behavior.__index = part -- Set reference to this object to part function behavior:__newindex(key, value) part[key] = value -- Set new index of this object to new index of part end -- Return new interface return setmetatable(interface, behavior) end -- Pass part to wrapper local wrappedPart = WrapPart(part) -- As you can see, we can access both our new property, and all the part's other properties. Giving us the illusion we added something to this object. print(wrappedPart.BirthTime) -- > Timestamp print(wrappedPart.Name) -- > "Part"
Thanks for the help.
Why interface and behaviour were specifically chosen as the name of the variable in the function.
Interface
simply refers to the functionality that you can use. behavior
holds the metatable, which dictates the behaviour of the table.
How the local part interacts with the part in the WrapPart function
The local part is passed in to the WrapPart function; the local variable part
could be just as easily moved to after the WrapPart
function is declared.
What behavior.__index = part does
Since behavior
becomes the metatable for interface
, this tells Lua that whenever you try to access a key in interface
that does not have a value, check for the key in part
instead.
You can look up what metatables do for more information:
You can set __index
to a table or a function; if you set it to a function, Lua will run that function to get the value to return.
setmetatable(interface, behavior)
tells Lua that you want behavior
to be interface
's metatable; the setmetatable
function also returns the first argument you gave it (interface
in this case). Thus, local wrappedPart = WrapPart(part)
effectively assigns interface
to wrappedPart
.
__newindex is the same idea, but for assignment. If, at the end of the script, you say wrappedPart.Name = "Hello"
, "Name" would not exist on the interface
/wrappedPart
table (both interface
and wrappedPart
reference the same table), so the function assigned to __newindex has code to redirect this assignment to the original part
passed in to WrapPart. The final results is that the original part would have the new name.
(To be clear, function behaviour:__newindex(key, value) ... end
is identical to behaviour.__newindex = function(self, key, value) ... end
)
In the code (line 26) wrappedPart.BirthTime
, BirthTime is already defined on the table (this is done on line 9), so __index is not involved. However, on line 27, "Name" was never defined on the table, so without a metatable, wrappedPart.Name
would return nil
-- but since we have a metatable with __index
defined, Lua checks (in our case) the original Part
, effectively performing part.Name
(which is why it prints out "Part").
I get most of it now but i still dont understand a few things.
How is it that the parameter of the WrapPart function, in this case, "part", can be the same name as the local "part" = Instance.new. I cant fully know that i understand lines such as behavior.__index = part because i cant tell which "part" it is talking about. Would it change anything if i changed the local "part" = Instance.new. to something like local "block" = Instance.new? Would it matter if i changed the name of the parameter in the function to something not defined yet? If the parameter of the function is simultaneously defined as a variable, how does that work?
Does calling __newindex on the list through "behaviour" add an item to "Interface"? Is that correct? I need clarification for exactly where that new index item is defined.
I have a few more questions still but hopefully those will be answered when someone explains the deal with the "part" variable/parameter.