Throughout looking at scripts, reading them, I've learned there are multiple ways to create functions, the most common being:
local function functionName() -- code end
or
functionName = function() -- code end
but one thing I've never understood is how people create functions or use them via :
or .
most commonly seen when tables are involved.
This is what I've been seeing:
local module = {} function module.something() end
or I think I've also seen
local module = {} function module:something() end
I don't understand the difference and how they work. Would someone care to explain and which one is better to be used?
Thanks for any help / feedback I receive, I personally have been using the first 2 as well as:
local module = { funcName = function() -- code end }
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.
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) --calling the function: a1 is our account and 10 is the value we want to subtract 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)
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 --account 1 a2 = Account --acount 2
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.
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 sayAccount.new()
this is creating a new account instance
the code would look like this:
Account = {} -- this is our account variable from the previous examples. notice there is no balance variable. --this is our function that creates a new Account. function Account.new(balance) --by saying setmetatable() we are creating a table inside `Account` that contains a balance variable set to whatever balance we specified in the parameters. --if we made two accounts: Account.new(12), and Account.new(99), visually Account would looks like this: --[[ Account = { {balance = 12}, {balance = 99} } ]] 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.
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 class: ------------------------------ 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 ------------------------------ --using the account class to make accounts (TeChNicAL term for a new account is an instance/objects) ------------------------------ a1 = Account.new(10) a2 = Account.new(9022) a1:withdraw(1000) --make sure you're calling the functions here with : not . or else self won't exist and it'll error a2:deposit(999) print(a1.balance) print(a2.balance)
**p.s. did u get your answer solved for the cframe server thing? **