Getting Classy With Lua

We’re going to talk about working with classes in Lua. I assume you’ve read the basics of Lua and about Object-Oriented Principles. We are going to gloss over some concepts (like metatables) and instead focus on getting the basics down.

To start off, Lua is not really a truly object-oriented language. Classes can be implemented in several ways and there are several libraries and extensions which add classes. Classes can be made with either tables or closures. This tutorial is going to focus only on the most basic cases for table based classes.

Syntactic Sugar

We’re going to be making use of some features in Lua which are known as “syntactic sugar”. Syntactic sugar is just a nice way of saying a feature which doesn’t necessarily change something in the language, but just makes it easier. We’re going to make a class and go over how to make it look nice using copious syntactic sugar.

Our First Class

classexample.lua:

#!/usr/bin/lua5.1

-- 'square' definition

local square = {}
square.__index = square --this makes our class work

function square.new(side)
	local self = setmetatable({}, square)
	-- internal processing for our class instantiation
	self.side = side
	-- end of internal processing
	return self
end

function square.getsideugly(self)
	return self.side
end

function square:getside()
	return self.side
end

function square:getarea()
	return self.side * self.side
end

-- Main:

local mySquare = square.new(5)

print( "square side: " .. mySquare.getsideugly(mySquare) )
print( "square side: " .. mySquare:getsideugly() )
print( "square side: " .. mySquare:getside() )
print( "square area: " .. mySquare:getarea() )

When you run it, you will get:

$ ./class.lua
square side: 5
square side: 5
square side: 5
square area: 25

Breaking It Down

local square = {}
square.__index = square --this makes our class work

To start off we set a table to be used for our square class. We use a metamethod to set the __index of our square table. To keep it short, we’re basically telling this that anything made with the square table inherits the square class’s definitions. This lets us define our functions and actually build a class. This is a very simplified view and there is a lot more to this, but for now, just use it. If you really want to know more right now, see this.

We kind of glossed over the metamethod piece for the time being, but let’s spend a little longer on the constructor. Most languages require a new keyword for a constructor, but Lua really doesn’t care.

function square.new(side)
	local self = setmetatable({}, square)
	-- internal processing for our class instantiation
	self.side = side
	-- end of internal processing
	return self
end

Here, we’re telling Lua that we have a function in our square class which is called new. We pass it a side scalar to work with for our definition.

local self = setmetatable({}, square)

This line sets us up for making a class. We make a variable called self which is our object. We then use setmetatable to set self to be an instance of our object. We process whatever we want with our object, then return self to return the object to be used.

We instantiate an instance of our object with the following:

local mySquare = square.new(5)

You don’t need it to be local, but it does help with scoping and garbage collection. We use the new definition in square to define our object and set the return to our local variable mySquare. Now we can start using our class.

If this doesn’t make that much sense, don’t worry about it. We’re using some really complicated concepts to do something which is normally pretty simple. We’re learning a common idiom which is based on some very complicated grammar and usage. Work to remember it and apply it now, and we’ll go over what makes it tick as we learn more about Lua.

The Ugly Duckling

function square.getsideugly(self)
	return self.side
end

I talked about syntactic sugar earlier, and this function showcases why we want it.

If we look down below in the program, we see the following:

print( "square side: " .. mySquare.getsideugly(mySquare) )
print( "square side: " .. mySquare:getsideugly() )

These both give the exact same result, but the second is definitely much cleaner than the other. In Lua, the difference between “.” and “:” is the implication. mySquare.getsideugly(mySquare) tells it to run the function as written and we explicitly pass our class so that our function knows what to do with it. The colon tells it to silently pass this without us having to care.

If we look at the other getside function, we see the following:

function square:getside()
	return self.side
end

We didn’t have to pass “self” to it. We have implied it with the colon.

print( "square side: " .. mySquare:getside() )

Doesn’t that look much nicer?

Getting Closure

class2.lua:

#!/usr/bin/lua5.1

-- 'square' definition

local square = {}

square.new = function(myside)

	local self = {}
	-- internal processing for our class instantiation
	side = myside
	
	self.getside = function() return side end
	self.getarea = function() return side * side end
	
	-- end of internal processing
	return self
end

local mySquare = square.new(5)

mySquare.side = 100 -- this doesn't do anything!

print( "square side: " .. mySquare:getside() )
print( "square area: " .. mySquare:getarea() )

Notice how we don’t use any metatables or anything in this method. This method is arguably cleaner, but isn’t really taught from the get-go. Functions are first-class in Lua so this method works, but it can be a bit weird for people from other languages. There are pros and cons, but the important thing right now is to just try them out and work with them. One thing to note is that this method allows private variables. Try setting the side in the class with the table method and see what happens!

Categories: Lua Tutorial
Some Dude: