Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

The different ways to create functions via Tables?

Asked by 4 years ago

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
}
0
I'm not sure if this answers your problem but : https://www.lua.org/pil/16.html User#20279 0 — 4y
0
It helps me understand it a bit more but I crave for a easier explanation. BlackOrange3343 2676 — 4y

1 answer

Log in to vote
2
Answered by
royaltoe 5144 Moderation Voter Community Moderator
4 years ago
Edited 4 years ago

The difference between module.myFunction() and module:myFunction() is that the : has an implicit selfparameter, 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)  --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)

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 --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.

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 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.

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 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? **

0
Very helpful, but I don't think I'm ready for this yet seems complicated without practice. And no the cframe thing is still unsolved ;-;. Thanks for helping. BlackOrange3343 2676 — 4y
0
That's okay. Can we possibly talk on discord I want to figure out why it isn't working royaltoe 5144 — 4y
Ad

Answer this question