Object-Oriented Programming

As Lua supports duck typing through the use of metatables, the ability to create "objects" can be achieved. This is essentially objected-oriented programming. A table in Lua is an object in more than one sense. Like objects, tables have a state. Like objects, tables have an identity that is independent of their values; specifically, two objects (tables) with the same value are different objects, whereas an object can have different values at different times, but it is always the same object. Like objects, tables have a life cycle that is independent of who created them or where they were created.

Operations on Tables[edit]

Tables can have their own operations:

Account = {
	balance = 0
}
 
function Account.withdraw (v)
	Account.balance = Account.balance - v
end

This definition creates a new function and stores it in field withdraw of the Account object. Then, we can call it as:

Account.withdraw(100)
print(Account.balance)

This kind of function is almost what we call a method. However, the use of the global name Account inside the function is a bad programming practice. First, this function will work only for this particular object. Second, even for this particular object the function will work only as long as the object is stored in that particular global variable; if we change the name of this object, withdraw does not work any more:

a = Account
Account = nil
a.withdraw(100) -- ERROR!

Such behavior violates the previous principle that objects have independent life cycles. A more flexible approach is to operate on the receiver of the operation. For that, we would have to define our method with an extra parameter, which tells the method on which object it has to operate. This parameter usually has the name self or this:

function Account.withdraw(self, v)
	self.balance = self.balance - v
end

Now, when we call the method we have to specify on which object it has to operate:

a1 = Account; Account = nil
-- Some logic here
a1.withdraw(a1, 100.00) -- OK

With the use of a self parameter, we can use the same method for many objects:

a2 = {balance=0, withdraw = Account.withdraw}
-- Some logic here
a2.withdraw(a2, 260.00)

This use of a self parameter is a central point in any object-oriented language. Most OO languages have this mechanism partly hidden from the programmer, so that she does not have to declare this parameter (although she still can use the name self or this inside a method). Lua can also hide this parameter, using the colon operator. We can rewrite the previous method definition as

function Account:withdraw (v)
	self.balance = self.balance - v
end

and the method call as

Account:withdraw(100.00)

The effect of the colon is to add an extra hidden parameter in a method definition and to add an extra argument in a method call. The colon is only a syntactic facility, although a convenient one; there is nothing really new here. We can define a function with the dot syntax and call it with the colon syntax, or vice-versa, as long as we handle the extra parameter correctly:

Account = {
	balance=0;
	withdraw = function (self, v)
		self.balance = self.balance - v
	end
}
 
function Account:deposit (v)
	self.balance = self.balance + v
end
 
Account.deposit(Account, 200.00)
Account:withdraw(100.00)
 
print(Account.balance)

Now our objects have an identity, a state, and operations over this state.

Constructors[edit]

So far, we've shown how to create a single once-use object. However, we might want to manage multiple accounts. Here's how we do it:

Account = {}
function Account.new(balance)
    return setmetatable({balance = balance}, Account)
end
Account.__index = Account

This creates the constructor Account.new(balance), which returns a new account object. The setmetatable makes it so the new account will look in the Account table for its metamethods, and the metamethod Account.__index = Account makes it look in the Account table for its methods. Let's add some methods then!

function Account:withdraw(v)
    self.balance = self.balance - v
end
function Account:deposit(v)
    self.balance = self.balance + v
end
function Account:__tostring()
   return "Account(" .. self.balance .. ")"
end

To use it:

local a = Account.new(100)
local b = Account.new(200)
print(a, b)
 
a:withdraw(20)
b:deposit(40)
print(a, b)
Account(100) Account(200)
Account(80) Account(240)

Another example[edit]

A custom object is something that you create that has properties like the other Roblox objects, such as game.Workspace.Brick.Transparency

Declaring an object[edit]

Your data type here will be a "Vector2".

Vector2 = {} --this declares the class "Vector2"
Vector2.__index = Vector2 
function Vector2.new(x, y)
	return setmetatable({x = x, y = y}, Vector2)
end

Now, we can call a new object of the class as such:

a = Vector2.new(7, 8)
print(a.x) --> 7
print(a.y) --> 8
a.x = 10
print(a.x) --> 10

Adding member functions to your object[edit]

function Vector2:dot(that)
	return self.x * that.x + self.y * that.y
end
function Vector2:magnitude()
	return math.sqrt(self:dot(self))
end
function Vector2:__add(that)
	return Vector2.new(self.x + that.x, self.y + that.y)
end
 
a = Vector2.new(3, 4)
 
print(a.x) --> 3
print(a.y) --> 4
print(a:magnitude()) -->5
 
a = a + Vector2.new(2, 8)
 
print(a.x) --> 5
print(a.y) --> 12
print(a:magnitude()) --> 13

See also[edit]