Alright, I have made a lot of classes and now I feel like I actually understand what metamethods almost fully instead of thinking of them like events. It also made me think about why an .IndexChanged meta method would be an paradox.
First of all we need to define the difference between an event and overwriting behavior and an callback.
First letâs talk about how events actually work. I would have never known this without making my own event library.
When you connect an event your basically adding this function to a table. When the :Fire() function is called all these functions are ran in seperate threads. For example when an part is touched, (roblox might do this by a loop for collision detection), and when a part is collided roblox might do this.
â LET ME ZOOM IN THE CODE THIS ISNâT OBVIOUSLY ALL OF COLLISION DETECTION JUST A SNIPPET
while true do
if Collision between part this and part that then
Make both parts collide
Part.Touched:Fire(PARAMETERS)
end
end
And any functions connected to it might be ran like this:
for i,v in pairs(Part.Touched.FunctionsToRun)
makeThread(v) --whatever running the function in a new thread
end
Now letâs explain what overwriting default behavior is. Events allow us to preserve default behavior while adding our own behavior when the event is fired.
For example before the .Touched event is fired roblox might make it so if a unanchored cancollide part is moving to an anchored canncollide part, they might make the part collide with it so it doesnât go through(not saying they do they might, but this is a reasonable approach ) for physics.
Ok now lets talk about what overwriting behavior is.Events are kinda like semi independence, but when you overwrite default behavior itâs TOTAL INDEPENDENCE!!! This time we wonât be using Part.Touched:Fire() that makes no sense for overwriting default behavior. Instead let me propose a new thing
function Part.OnTouch()
Make parts collide
end
while true do
if Collision between part this and part that then
Part.OnTouch()
end
end
and when we do this
Part.OnTouch() = function() print âToUCHEDâ
We are overwriting all behavior, so now the physics engine would actually collide the parts anymore itâll only print touched. This is useful for total control.
Now finally calllbacks. I like to think of CallBacks as an mix between total control and no contro. (Eventâs donât give you control on whatâs happening internally they just allow you to attach your code as part of the process when something happens).
function part.OnTouch()
return âLOLâ
end
while true do
if Collision between part this and part that then
Collide Parts
local Result = Part.OnTouch()
print(Result)
end
end
See here if we overwrite the .OnTouch function itâll still collide the parts. Here we are using the returned result of the callback to do something and using the returned value unlike events. Sure it isnât full control but itâs more control the events.
So finally what are methamethods actually doing!!!?? They are overwrting default behavior. When the lua Interpreter sees a metatable attached, and seeâs a meta method corresponding to the action done to the table it does that instead of the default behavior.
https://gyazo.com/07548033edeef9ae27ed8a5516f666ff
Now how it does this can be done many ways. For example, it can do the way I did above
function Part.OnTouch()
Make parts collide
end
while true do
if Collision between part this and part that then
Part.OnTouch()
end
end
and when we do this
Part.OnTouch() = function() print âToUCHEDâ
We are overwriting all behavior, so now the physics engine would actually collide the parts anymore itâll only print touched. This is useful for total control.
Or it could be done using a different way such as using if statements. But the concept remains.
Now letâs talk about the infinite recursion paradox with an .IndexChanged event.
local Master = {
__IndexChanged = function(Table, Index, Value)
Table[Index] = Value -- we need to do this because ALL original behavior is overwrited so we need to manually set it
-- UH OH now the statement above fires .INDEXCHANGED event again, oh no process repeats and repeats and repeats
Table.OnNestedTableChanged()
end
--- doesnt exist just an example how it would be if it did
}
local Table = {
["HowManyNestedTablesYouHaveTable?"] = 'IDK'
}
Table["HowManyNestedTablesYouHaveTable?"] = 'actualy i think it is 5' -- ok INDEXCHANGED metamethod fires
Table.OnNestedTableChanged = function()
print 'yayyyy'
end
setmetatable(Master, Table)
So this is why we use proxies.
With events this paradox wouldnât come since when you do
Table.HowManyNestedTablesYouHaveTable["Cool] default behavior would be preserved and the event would just trigger when changing an index would happen internally in lua.
Ok to sum it up methamethods are overwriting what the lua interpeter actually does when an action is done to a table. I hope this helps cause I used to be thinking that metamethods were events but actually they arenât based on the defintion I said above.
Oops I forgot that RawSETand those stuff existed. If IndexChanged was added then there might be RAWCHANGEINDEX, so the infinite recusion problem might not be a problem anymore.