So I’ve been learning about metatables and how to use OOP in Roblox module scripts. I do understand them, however, I’ve seen many different ways of doing it and I wonder if there is any difference from them. I’ve been trying to google it and understand, but with no luck and I wonder how is the best way to utilize metatables in Lua? I’ve stated some of the different methods below.
Module 1
local module = {}
function module.new()
local self = {}
setmetatable(self, { __index = module })
end
return module
Module 2
local module = {}
module.__index = module
function module.new()
setmetatable({}, module)
end
return module
Module 3
local module = {}
function module.new()
setmetatable({}, self)
self.__index = self
end
return module
From what I’ve learned, self is a special variable that refer to the current instance of the object, but as you can see in Module 1 and Module 3, self is used differently. In Module 3, you set self to be the metatable for the object, whilst in Module 1 you set self to be the object that gets a metatable attached (Correct me if I’m wrong, please). This confuses me a lot, and I would appreciate advice on this, what to use and the maybe the difference.
Approach one is not preferable because it creates a new metatable when it can reuse the same one.
Approach three doesn’t work at all.
Approach two is the common way to do it. It’s possible to deviate from it certainly but that approach is simple.
Also, you forgot a return on all of the examples, so the above statements assume this return is present.
Lastly, self is just a variable name. What’s peculiar about it is when you define a function using : syntax instead of . syntax it makes self the first parameter automatically. This makes it useful specifically for method definitions that take a state.
Likewise, calling a function with : will automatically pass whatever is to its left as the self parameter.
self is automatically passed as the first argument when you use methods, with : syntax.
For example :
local container = {}
function container:AddIndex(...)
for _, v in ipairs({...}) do
table.insert(self, v) -- essentially table.insert(container, v)
end
end
container:AddIndex("str", "str2")
-- print(#container) --> 2
You can use the __index metamethod so that if an index was not found in a table, Lua will refer to the metamethod; a shared metatable does not share any keys except from the metamethods, for instance
local Contents = {Name = "Part", Anchored = false}
print(Contents.Name) --> Part
local met = setmetatable({}, Contents)
print(met.Name) --> nil, keys not shared
Contents.__index = Contents
print(met.Name) --> prints Part
Luna isn’t perfect. I wrote many years ago, before I had the understanding of Lua that I do now. I would like to rewrite it, and preserve the syntax, but also reduce the complexity between public, private, and static variables.
Random metatables comment:
Tables can be their own metatables, and in fact this is often done on Roblox specifically, because when
marshalling tables, the metatable is not sent.