The difference between module.myFunction() and module:myFunction() is that the : has an implicit self parameter, where with . you have to explicitly define self in the parameter of a function. The fancy word for : is a method where . is a function. The words mean different things, but you'll hear them used interchangeably a lot of the time.
Self is a reference to the table that your function is inside of. It's very much needed in something called Object Oriented Programming (OOP) which i'll explain in a sec.
All the examples i'm about to explain are from the roblox wiki. roblox took these examples from lua's website and explained it a bit more.
The examples the websites use explain how to make a bank script. The bank script holds all the accounts and their balances in a table, and each account has a function to withdraw money from that account.
Part 1 will give you an understanding of self, part 2 will explain why oop is useful and the other parts will tell you how to make the script using self and OOP. T
btw, after you're done reading this you might wanna check out this tutorial: https://devforum.roblox.com/t/all-about-object-oriented-programming/8585
instead of a normal script it's using module scripts allowing you to create an object from any script.
#PART 1: UNDERSTANDING SELF
Anyway, we know tables can contain functions:
you can write a function inside a table like this:
Account = {
balance = 0
}
function Account.withdraw (v)
Account.balance = Account.balance - v
end
or like this:
Account = {
balance = 0,
widthdraw = function(v) Account.balance = Account.balance - v end
}
both ways allow you to call the widthdraw function like this.
Accout.widthdraw(10)
print(Account.balance)
the practices are basically the same thing, except the second example is called an anonymous function - a function inside a variable instead of a regular function.
i'm just going to stick with the first way of writing it because it's easier for me to read. instead of saying Account.Balance, one thing we can do is pass in account (self) through the parameters
function Account.withdraw(self, v)
self.balance = self.balance - v
end
here's the whole code to reflect this change. you can see that by using self we can refer to our account table and anything inside the account table.
p.s. the variable self can be named whatever you want it to be named, you could name it shrek if you want.
Account = {
balance = 100
}
function Account.withdraw(self, v)
self.balance = self.balance - v
end
a1 = Account
a1.withdraw(a1, 10)
print(a1.balance)
passing in the Account table every time you want to call withdraw() is kinda annoying isn't it? with : we don't have to. Here's the code above, but with using : instead of .
if you look at withdraw, you can see that there is variable named 'self' this is the same variable that we used in the last example, but : makes it so you can use the keyword without having to pass in self in the parameters every time we call the function.
Account = {
balance = 100
}
function Account:withdraw(v)
self.balance = self.balance - v
end
a1 = Account
a1:withdraw(10)
print(a1.balance)
#PART 2: Why Object Oriented Programming?
You might be thinking, why is this useful? I can just say the following?
Account.balance = Account.balance + 1
You're kinda right about that. There's no reason to use it if you just had one account.
But what if you had multiple accounts?
Try creating multiple accounts:
a1 = Account
a2 = Account
Here's the full code if you wanna try it out yourself:
Account = {
balance = 100
}
function Account.withdraw(self, v)
self.balance = self.balance - v
end
a1 = Account
a2 = Account
a1.withdraw(a1, 10)
a2.withdraw(a1, 20)
print(a1.balance)
print(a2.balance)
This happens because a1 and a2 are both sharing the same table. If you print a1 and a2, you'll see they both print out the same thing (Account's memory address). This means that a1 and a2 are both references to the Account table. They share the same table therefore they share the same balance variable.
We instead want a1 and a2 to have a copy of the account table, but how do we do that?
One option is to create another table with the same functions / variables as the account table which would look something like this:
Account1 = {
balance = 100
}
function Account1.withdraw(self, v)
self.balance = self.balance - v
end
Account2 = {
balance = 100
}
function Account2.withdraw(self, v)
self.balance = self.balance - v
end
a1 = Account1
a2 = Account2
a1.withdraw(a1, 10)
a2.withdraw(a1, 20)
print(a1.balance)
print(a2.balance)
but we want to be able to make accounts in game, we can't edit our script while the game's running!! :( what if we want to make changes to account? we'd have to copy and paste the changes into every account variable.
This is why OOP is handy. With OOP, we can create a copy of the account Account table without copy and pasting like in the last example. This means we could make 5 copies... 10 copies... 100,000 copies if you'd like. as long as ur computer allows for it.
#PART 3: CREATING A CLASS
In OOP, there's something called a class. A class like a cookie cutter and instances are the cookies getting made by the cookie cutter. If I have a gingerbread cookie cutter and i want to make loads of ginger bread, I would press the cookie cutter into the dough and then my dough that i just pressed gets shaped into a gingerbread dude. i can make as many ginger dudes as i'd want, but i am creating them with my cookie cutter - my class.
to make a new account, we would would say Account.new() this is creating a new account instance
the code would look like this:
Account = {}
function Account.new(balance)
return setmetatable({balance = balance}, Account)
end
Account.__index = Account
Some terms that are probably new to you:
setmetatable - makes it so that the new account will look in the Account table for its metamethods, and the
Account.__index = Account a metamethod that looks in the Account table for its methods and puts them into the metatable that we created with set metatable.
You might have noticed we don't have any methods (aka functions), so let's add some.
function Account:withdraw(v)
self.balance = self.balance - v
end
function Account:deposit(v)
self.balance = self.balance + v
end
When Account.new() is called, __index puts the functions above into the metatable we just created. Notice how all these functions have self. This is where self gets useful. We have multiple account objects so we can't say Account.balance to get the balance anymore. We need to specify which account we're talking about. Since : allows us to refer to our table that is holding our function, self refers to the individual account.
#PART 4 USIN' CLASS TO MAKE STUFF
don't think i made it clear how to create accounts using the account class we just made.
here's how you'd do that:
Account = {}
function Account.new(balance)
return setmetatable({balance = balance}, Account)
end
Account.__index = Account
function Account:withdraw(v)
self.balance = self.balance - v
end
function Account:deposit(v)
self.balance = self.balance + v
end
a1 = Account.new(10)
a2 = Account.new(9022)
a1:withdraw(1000)
a2:deposit(999)
print(a1.balance)
print(a2.balance)
**p.s. did u get your answer solved for the cframe server thing? **
2 comments
I'm not sure if this answers your problem but : https://www.lua.org/pil/16.html
It helps me understand it a bit more but I crave for a easier explanation.