Module.new() and Module:New()

I’ve been trying to learn what metatable is for days and I still can’t find anything that I understand. People just keep on saying different things in each post and tutorial. So these are some scripts I found on metatables and some questions I have about them!

local TestModule = {}

function TestModule.new(Data)--what does .new do?
   local Table = setmetatable(TestModule,Data)--What is Data and why not use self? Also why don't we use {} for TestModule?
   Table.Data = Data
   print(self.Data)
   return Table
end

function TestModule:ChangeData(NewData)
   self.Data = NewData
end


return TestModule
local base = {}
base.__index = base--????????(What is __index?)

function base:New()--:New() is a function we created and it will create a child object of this base(metatable)
	local t = setmetatable({}, self)--self=the object we made
    --What is self and why are we parenting the metatable to itself?
    t.__index = t --???
    return t
end
return base --How will other scripts use New()?
2 Likes

You should check out the metatables documentation. It shows you great examples of what metatables and metamethods are.

Well I already checked those, and still, people are saying different things in tutorials and posts than each other! So I really don’t know where to go anymore.

1 Like

Documentation would be the best source since it’s coming from Roblox itself.
You can also check out Luau’s documentation itself to learn more about the language.
Also, you can ask questions here if you want to! I and many others are pretty experienced in metatables, so don’t be afraid.

1 Like

I think what you’re looking for is an explanation of object oriented programming in Lua. Here’s a great guide/introduction to it All about Object Oriented Programming.

Whenever you see a function defined like that, it’s just defining a function inside a table.

function module.new()

end

--is the same as

module.new = function()

end

-- which is the same as

local module = {
    new = function() end
}

I looked through the documentation a couple times and I kind of understand it a little bit. On the post I put questions there if you are willing to answer them? And also, what exactly are metatables FOR? I’ve seen people make furniture building systems and I really don’t understand what that has to do with metatables. And isn’t there a better and easier way to make things without using metables?

Metatables are basically tables with some powerful methods attached to them.
Example:

local testTable = {
   money = 100
}

local methods = {} -- We declare a methods table to store our methods in

-- This method will be run when table[index] is nil. For example, if we do testTable["age"] because we don't have an "age" key in the table, this function attached to the method will be run.
methods.__index = function(table, index) 
   -- the "table" value is the table that has been indexed.
   -- the "index" value is the value that the table has been indexed with. 
   -- for example, testTable[1] would have testTable as the table and 1 as the index
end

testTable = setmetatable(testTable, methods) -- We create a new metatable with methods attached to the testTable. 

-- you can do print(testTable["age"]) to test if the method runs.

There are many more methods out there that you can use as well.

Now, the main reason why we use metatables is that lua and luau do not have a direct way of creating OOP classes and objects. Basically for Object Oriented Programming, we use metatables to create classes and objects.

There are some neat tutorials out there that can show you what OOP is and how metatables are used for it.

Forgot to mention, self isn’t directly related to metatables.
It’s basically a variable that represents the object or the method itself.
For example:

function object.Method(a, b)
     print(self) -- nil
end

function object:Method(a, b)
    print(self) -- will print the object itself
end

object.Method("epic") -- If the function is defined in the second way, self will be "epic"
object:Method("epic") -- If the function is defined in the second way, self will be the object, though if defined in the first way, it will still be nil

Just to be a little more complete in what self is

The colon secretly adds a parameter to the first of the list called self. You can insert it yourself with . notation instead. The following 2 methods are identical and I show how to call them both below

function object.Method1(self, a, b) --added self manually as first parameter.
     print(self) -- will print the object itself
end

function object:Method2(a, b) --secretly added self as first parameter
    print(self) -- will print the object itself
end

object.Method1(object, “a”, “b”) --have to add object as first parameter manually
object:Method1(“a”, “b”) --secrety adds object as the first parameter

object.Method2(object, “a”, “b”) --have to add object as first parameter manually
object:Method2(“a”, “b”) --secretly adds object as first parameter

when running functions, you either index using dot or colon

using dot is when you want the arguments set as the parameters in order

while using colon is that you’re setting the first argument as the second parameter, the second as the third parameter, and so on

basically, the colon is like this

module:new("hi")
-- is the same as
module.new(module, "hi")

the colon is mainly used in modulescripts and Object-Oriented Programming (OOP) if you want the self variable. the self variable is the table parent of the function

function module.new()
    print(self) -- prints "nil" meaning doesn’t exist
end

function module:new()
    print(self) -- prints the table module
end

making a function using the colon is the same as this

function module:new()
    
end
-- is the same as
function module.new(self)
    
end

Ignore the first script in the example you gave, it’s going to be messed up if you use TestModule.new() more than once…

Just want to add on to this for @BlueBloxBlast :

Metatables are really just tables that contain metamethods.

The easiest way to look at metamethods is by treating them as events that fire whenever you do something to a table that you applied said metatable to.
For example, if you tried indexing (accessing the index of) a table that does not exist, the metamethod __index (that you will bind to a table or a function in your metatable) will fire.

  • If it is bound to a table, that table is indexed.
  • If it is bound to a function, that function is called.

__index is the foundation of OOP; there’s a plentiful of guides that elaborate on this further already.

Hopefully that helps clear things up.

for normal uses, module.new() and module:new() aren’t that different

for OOP however

:new(…) is the same as .new(self, …), where self is the module itself

with metatables, you can do this

local module = {}
module.__index = module

function module:Function1()
    -- idk
end

function module.new()
    local customObject = {}
    -- you can add your own properties
    return setmetatable(customObject, module)
end

return module

now, when you call module.new(), it returns customObject
and customObject can kind of “inherit” the functions in module, what i mean is you can do customObject:Function1()
and now, the hidden self argument is customObject instead of module
pretty cool, and useful

so when you do module:Function1(), self is module. when you do customObject:Function1(), self is customObject and you can do customObject:Function1() thanks to the __index metamethod

They let you override default behavior that tables normally have. Or add some behavior that’s not there by default. I’m going to focus on the override part here.

Let’s look at this dictionary here

local dict = {
    x = 5,
    y = 2,
    z = 5
}

Now say we want to look something up. We could of course just call dict[“x”] which would return us the value stored under the key X. What’s important to know though is what happens if the thing we try indexing doesn’t exist?

dict[“pancakes”] searches for it and immediately returns nil when it can’t find it right? The answer is kind of. It actually makes one more stop after it can’t find it. It checks the metatable to see if you’ve overriden the default behavior of just returning nil.

So let’s look here

local example = {}
example.__index = example --what?

That, doesn’t make a ton of sense at first glance right? But what you are doing is pretty much exactly what it says. You’re making it reference itself with a “.__index” key. So now if you look that up, you’ll get the table itself returned. At this stage it doesn’t make a whole lot of sense why we would do that though.

So let’s move onto the next part

function example.new()
    local t = {}
    setmetatable(t, example)
    return t
end

Now we have set a metatable. So now t has a metatable. Which means now, when you try indexing t (t[“pancakes”]). If it finds t doesn’t have a key that matches it, it will then look at it’s metatable for instructions on what to do next. Specifically since you are indexing it, it’s going to look at the key __index. If the key is a function, it will run that and return whatever you return. So remember, “example” was the metatable it’s going to be looking at for more instructions. So if you were to change the second line to be

example.__index = function()
    return true
end

Now t[“pancakes”] would find that there is no “pancakes” in t, then look at example to see that it has a function to run instead of returning nil like it would by default. But if you recall, we set it as a reference to it’s own table. So this means that if it doesn’t exist in t, it’s going to look inside example next before it checks examples metatable for further instructions.

And that’s basically it. It let’s you override specific behaviours of tables. There is a list in the documentation of what you can override and some things to consider.

As for in practice, consider a design where you want to create lots of data. Say for example vector3s. Each vector 3 is going to have some functions you want to be able to use it with. Like :Lerp(). You could just put that inside the vector3 table of course, but that’s a waste of memory. It’s better to have a table of functions that get checked if you try to call something that doesn’t exist inside vector3 so that each vec3 can reference the same functions instead of storing its own copy.

to add on about __index

if example.__index is a table and you have something like this:

example.__index = {blah = "index"}
example.blah = "normal"

example.blah is always “normal”
but when normal example.blah turns to nil, doing example.blah returns “index” instead

ofc, "normal" and "index" can be anything else

1 Like

Gonna say it simple here. So __index is basically like a way to get static variables/methods, say that the dot operator (.) on a module script basically acts as a static identifier (same applies for __newindex). I don’t know how to properly say this but it’s something like this.

local module = {}
module.__index = module
module.SomeStaticMessage = "Hey!"

function module.new()
  local self = {}
  setmetatable(self, module)
  return self
end

local class = module.new()
print(module.SomeStaticVariable, class.SomeStaticVariable) -- "Hey!", "Hey!
-- If __index is not present, we cannot access <SomeStaticVariable> with <class> as it'll instead return nil.

Ofcourse this is basically a scratch on the surface about metatables but a quick fun fact is that __index also supports functions, with the 2 parameters being self and key where self is the class and key being the one indexed—that means you can add in more customizations on how metatables like that works, heads up, with the function version of __index, you have to return something, either the value being indexed or something else, also if you get a C-Stack overflow error, you should opt using rawget() instead. (__newindex takes in 3 parameters, self, key and value)

The title is slightly misleading, but a metatable is just basically hooking functions for specific table actions, such as reading (myTable.something, myTable["some thing"], etc…), writing (myTable["a thing"] = 123), comparing (myTable == otherTable, myTable > otherTable) and others.
Simply put, functions that are called when you do something to a table.

The examples you’re providing are basically module scripts, which basically provides methods for creating new objects and/or configuring their behavior.
Another detail is that : (namely namecalls) is syntax sugar, which i believe it’s reducing code writing as an alternate way.

The best that i can explain on each thing is:

  • Namecalls - Object:Method(...)
    Basically uses the first argument of the function to be a reference to the table itself, which is accessed via the self variable.
    Which doing myTable:Method(123, "abc") is equivalent as myTable.Method(myTable, 123, "abc").
    This is frequently used by userdata objects (Instances, Color3, Vectors, etc) and Object-Oriented Programming.
  • Singleton patterns and Modulation - MyModule.new(...)
    string, table, Color3 and other libraries are technically named as Singletons, since they only serve to store functions or values that help the user to create objects or use on other variables without polluting your codebase.
    In this case, you can also create your own Objects by using tables as if they were “Objects”.
    But instead of initializing a table with all the indexes defined in your main script, you can write a singleton in ModuleScripts, which can be retrieved via require.
    The new method is the constructor of said object, which basically handles everything related to creating your table with all the defined indexes of your preference.
Example

Like as said before, instead of doing this in your main script:

-- MAIN SCRIPT
for i=1,15 do
   local myObject = {
      Size = 15;
      Type = "Foo";
      Layer = i;
   }
   function myObject.Method()
      if myObject.Type == "Bar" then
         myObject.Size /= 2 -- divides the object's size by half.
      end
   end
   
   if i%2 == 1 then
      myObject.Method()
   end
   print(myObject.Size) --> 15 for evens, 7.5 for odds
end

You can reduce it into a Singleton that does the job on creating your object, making your main script much more cleaner to read.

-- MODULE SCRIPT (Parented into the main script as "MyModule")
local Singleton = {}

function Singleton.new() -- We're writing a constructor for your custom object!
    local myObject = {
      Size = 15;
      Type = "Foo";
      Layer = i;
   }
   function myObject:Method() -- "self" is "myObject"
      if self.Type == "Bar" then
         self.Size /= 2 -- divides the object's size by half.
      end
   end
   return myObject
end

return Singleton
-- MAIN SCRIPT
local MySingleton = require(script.MyModule)
for i = 1,15 do
   local myObject = MySingleton.new()
   if i%2 == 1 then
      myObject:Method() -- This will error if it's called via "." instead of ":"
   end
   print(myObject.Size)
end
  • Metatables (and Metamethods)
    This is basically a Man-in-the-middle functions table, which intercepts when you try to do something with a table.
    Whenever you try to write something to a table (MyTable.Foo = "Bar"), Lua checks if there’s a __newindex function defined in the table’s metatable when there’s nothing in the same given index.
    If there’s a function and Foo is nil in the table, it gets called by giving the following arguments in order:
    The self argument is the table itself you’re attempting to write on (MyTable)
    index is the name you’re trying to write into (MyTable.Foo)
    And value being the value you’re assigning with (MyTable.Foo = Bar)
    Otherwise it will be written as if it was normal.

Mixing all of this will make the base code way better to read, optimize and manage around!
It will take practice to learn all of this, but it’s worth doing so.

1 Like

Hey OP, while I don’t know how things such as __index work regarding metatables, there is one huge advantage to them. You can use any functions inside of the table regardless of order along with being able to organize it better.

Here’s an example of how it would work

functionTable = {}

function functionTable:first()
    self:second()
end

function functionTable:second()
    print(“yippee”)
end

—Calling the first function will print yippee

If you’re confused to what self is, it’s basically just another name for the table that is given by default. You can also just say functionTable:second()

This is essentially what a module script is, just a meta table that can be used in any script that has access to it.

When it comes to your question regarding using “:” or “.”, it’s just based on preference. I recommend using “:” however since it distinguishes functions from values and helps the autofill give better suggestions.

Edit: I should also mention that you can call a function from a table using “:” or “.” If you used “:” to set up the function.

1 Like

The examples you have given are wrong, here are the corrected ones:

local TestModule = {}
TestModule.__index = TestModule

function TestModule.new(Data)
   local Table = setmetatable(Data, TestModule)
   Table.Data = Data

   print(self.Data)

   return Table
end

function TestModule:ChangeData(NewData)
   self.Data = NewData
end

return TestModule

And

local base = {}
base.__index = base

function base:New()
	local t = setmetatable({}, self)

	return t
end

return base

Both these modules do the same exact thing, create a module and set meta table to the base class. In your code in the .new() version, you used self which will actually error, self wasn’t referenced anywhere and . doesn’t pass self (unlike :), but in the second version :New() you used self which actually works and is equivalent to local t = setmetatable({}, base)
__index is a metamethod, you said you read about metamethods so you should understand but in case you don’t, it makes the code look through the main table when indexing stuff, for example, if we take the first version (.new()), table “Table” does’t have the method :ChangeData but setting the metatable to the main module TestModule (which has the method) makes it work.
When creating classes you normally access methods using the : (class:Method()) so it’s always better to keep the new() aka the constructor with . because, how will u create an instance from a class using another instance (Except of it’s like :Clone() or something)?

local Class = require() --path here

local instance1 = Class.new() -- makes sense
local instance2 = instance1:New() -- Does this really make any sense? Ur creating a class without using the actual class

Although, u can still use both and get the same results, I personally use .new() as I think it’s better to use.
If something doesn’t make sense, feel free to ask.

Moudle.new returns nothing built in with it

Module:new returns itself with it(thus creating the self keyword)