Lua is a really simple programming language focused on portability and being able to be embedded, so chances are you won’t be using lua directly but as a part of a bigger program that embeds lua and allows you to use it to interface with the program.
Lua is really easy to learn, especially if you already have a programming background which I assume you do in this post as I will just explain how lua does it differently.
I’ve included a list of many concepts, feel free to jump around the table of contents, as a programmer you should be able to easily understand any part to learn what is relevant to you, especially if you already have some lua experience.
Warning
This tutorial is incomplete, notably I did not explain metatables and a few more concepts I wanted to explain. I will eventually edit this post and complete it.
Hello, World!
Let’s start with a Hello, World to begin:
print("Hello, World!")
So far simple, Python programmers will immediately feel familiar.
Comments
Comments are done via double dashes --
anything after it is ignored until the end of the line.
-- This is a comment.
-- Let's print some text.
print("Hey!")
Multiline comments are supported via delimiters of --[[
and ]]
--[[
This is a multiline comment.
It can span multiple lines.
]]
print("Hello, World!")
Primitive Types
We can use the function type
to check the type of a value.
The following are the types supported in Lua, more on them later.
-- numbers: type(5) == "number"
5
5.2
-- nil: type(nil) == "nil"
-- This is the empty type, like null in other languages.
-- like None in Python.
nil
-- strings: type("hi") == "string"
"Hello, World!"
-- booleans: type(true) == "boolean"
true
false
-- tables: type({}) == "table"
{}
-- More on tables later.
-- functions: type(function() end) == "function"
function test() end
Variables
Variables are simple:
x = 5
name = "john"
age = 17
Local Variables
One important thing to note is that all variables are global by default
We must explicitly mark variables as local
local name = "andrew"
Variables are by default nil, there is no undefined error:
randomName -- this is simply nil
All global variables are stored in a global table called _G
and any variable access is like accessing that table. Locals on the other hand is more optimized internally so generally local variables are faster and you should try to use them more if possible, a common lua pattern is to “cache” the things you will use in a local
variable and use that.
local sin = math.sin
print(sin(90))
You avoid a global variable lookup for math
and a property access to sin
and just access the local.
This type of performance things are small of course but it counts when you need to squeeze the most performance out of it, especially if you are writing something like a game loop.
Multiple Variables
It is allowed to assign multiple variables at the same time:
x, y = 5, 2
print(x) -- 5
print(y) -- 2
local sin, cos = math.sin, math.cos
print(sin(90))
print(cos(90))
Increment & Decrement
Note that Lua does not have any increment/decrement operators that you are used to in other languages like i++
, i += 5
, etc.
So you always do this the classic way:
num = 5
-- Increment the number by one.
num = num + 1
Strings
Strings can be made in 3 different ways:
Single Quotes:
'Hello, World!'
Double Quotes:
"Hello, World!"
Double Brackets:
[[
Hello, World!
This method allows multiline strings.
Text that spans multiple lines.
Not to be confused with multiline comments, there is no --
]]
And of course the classic escape sequence stuff like \n
are supported, like any other language.
Length of a string.
You can get the length of a string in 3 different ways:
With the #
operator:
local str = "hello"
print(#str) -- 5
With string.len:
print(string.len(str)) -- 5
With string.len as a metatable method:
print(str:len()) -- 5
More on metatables later.
String concatenation
A unique feature of Lua is the ..
operator which is the concatenate operator.
Instead of using +
for concatenating strings we use this:
print("Hello " .. "World") -- Hello World
String Conversion
We can convert any value into a readable string with the tostring
function:
local str = tostring(nil)
print(str) -- "nil"
print(type(str)) -- "string"
Control Flow
Now let’s take a look at the control flow stuff.
Operators
The operators we would can use are the same stuff like any other language. >
, <
, >=
, <=
, ==
.
There is and
and or
rather than &&
and ||
that you might be used to in other languages.
Negations are done with not
rather than !
Which should all be familiar if you are coming from Python.
An important thing to know is the not equal operator, you are used to !=
in other languages but Lua does this differently and uses ~=
instead, so get used to that.
If Statements
These are also easy:
if true then
print("Hello, World!")
end
Of course you would usually use a variable:
x = true
if x then
print("Hello, World!")
end
It is worth nothing that only false
and nil
are falsy, everythinf else is truthy, including 0
, unlike other languages:
if 0 then
print("This works!")
end
Like any other language we got else
if available then
doStuff()
else
doOtherStuff()
end
And multiple branches with elseif
if one then
doStuff()
elseif two then
doOtherStuff()
elseif three then
doTheOtherThing()
else
doWhatever()
end
While Loops
While loops are the same:
while condition do
runCode()
end
Repeat Until
Another loop construct is the repeat until:
num = 10
repeat
num = num - 1
print("Hello")
until num == 0
Will run the loop until num
becomes 0
For Loops
For loops start at an initializer, a destination number and an optional step.
for i = 0, 100 do
print(i)
end
This prints numbers from 0
to 100
Note that the target is inclusive and unlike other languages the 100
is also printed.
We can add a step to change how numbers move:
for i = 0, 100, 2 do
print(i)
end
This prints the number and keeps moving by 2
so 0, 2, 4, 6, ...
We can use this to count backwards with a negative number:
for i = 100, 0, -1 do
print(i)
end
This prints numbers starting from 100
and counting down all the way to 0
Functions
We finally we get to functions. These are also simple:
function sum(x, y)
return x + y
end
print(5, 2) -- 7
- You return values with
return
. - Undefined parameters are simply
nil
there is no argument count errors or anything. Similar to JavaScript’s arguments beingundefined
Code After Return is illegal
With the way Lua’s syntax is built, it is illegal to write any code after a return:
function test()
return 5
print("This breaks.")
end
In other languages that would be considered “dead code” and that part will simply never run, but in Lua that is invalid syntax and your code won’t run.
This permits the return value to be in a different line:
function sum(x, y)
return
x + y
end
print(5, 2) -- 7
This works as expected, our first return
did not return anything so it followed the next line for the value.
Returning Multiple Values
It is possible to return multiple values and we can use them with multiple assignment:
function getColor()
return 255, 150, 20, 180
end
r, g, b, a = getColor()
print(r) -- 255
print(g) -- 150
print(b) -- 20
print(a) -- 180
It is possible to only use the values you need, the rest will just be “discarded”:
r, g = getColor()
print(r) -- 255
print(g) -- 150
The return value of these functions are automatically expanded to multiple input parameters if we pass them to another function:
function sum(x, y)
return x + y
end
function getValues()
return 5, 2
end
print(sum(getValues())) -- 7
Function Scopes
Note that the same scoping rules as a variable applies to functions too, currently we’ve been creating global variables with the function, we can mark them local
too:
local function stuff()
return 5
end
This is from the fact that defining functions is basically defining a variable and so you can also do:
test = function()
return 5
end
local stuff = function()
return 5
end
Tables
This is the main big thing in Lua and is the most powerful thing in the language.
At a simple glance, they are hash tables, dictionaries, maps, associative arrays or whatever you call them. They just map a key to a value.
Let’s get used to the basic syntax and I will go over more of their functionalities after:
-- Create a table
local user = {
id = 5
name = "john"
age = 17
}
And then accessing the values:
-- Two ways to access the table
print(user.name) -- "john"
print(user["name"]) -- "john"
-- We usually use the dot access but
-- the second method is useful for dynamic access
key = "name"
print(user[key]) -- "john"
We can also modify the table and add more stuff later as needed:
user.location = "London"
-- Or the second method
user["location"] = "London"
print(user.location) -- "London"
Like variables undefined keys are simply nil
:
print(user.randomStuff) -- nil
So by setting a key to nil
we are essentially deleting it:
user.location = nil
This key no longer exists, this also applies to when we loop through all keys, it won’t be there.
Looping Through Tables
We can use the pairs
function to get an iterator of key-values to a table:
for key, value in pairs(user) do
print(key .. ": " .. tostring(value))
end
This should print:
id: 5
name: john
age: 17
Tables as Arrays
Lua does not have a specific array type like other languages, instead it reuses the concept of tables to create arrays, with the keys being numbers to represent the array index.
We can create one by making a table with elements like so:
local list = { "apple", "banana", "orange" }
Then we can access indices, keep in mind that in Lua array indices start at 1
instead of the usual 0
!
print(list[1]) -- "apple"
print(list[2]) -- "banana"
print(list[3]) -- "orange"
Length of arrays
We can use the #
operator to get the length of an array table:
print(#list) -- 3
Looping through arrays
With arrays starting at 1
it should now also make sense why for-loops’ target is inclusive. It makes it less confusing to write loops for arrays:
for i = 1, 3 do
print(list[i])
end
Or using the #
operator:
for i = 1, #list do
print(list[i])
end
Another way to iterate through arrays is the ipairs
function, similar to the pairs
we saw for key-values except this returns array indices for us instead of keys:
for i, v in ipairs(list) do
print(tostring(i) .. ": " .. v)
end
It should print:
1: apple
2: banana
3: orange
Inserting and Removing from an Array
We can use table.insert
to add elements into an array:
table.insert(list, "kiwi")
This inserts an element into a new position, now list[4] == "kiwi"
We can also insert into a specific position, anything in that position will be shifted up into the next position:
table.insert(list, 1, "pear")
This adds pear
into position 1
the first element, so now the entire list shifts up and the items are now:
1: pear
2: apple
3: banana
4: orange
5: kiwi
We can use table.remove
to delete elements:
-- table.remove alone will "pop" the last element and give it to us.
value = table.remove(list)
print(value) -- "kiwi"
kiwi
is now also removed from the table.
We can give it a specific position to remove which will cause the table to shift down:
table.remove(list, 1)
That will remove the previously added pear
and shift down the elements and so we are back to what we had:
1: apple
2: banana
3: orange
Packing and Unpacking Arrays
We can use table.pack
to create a table array from multiple paramaters:
local fruits = table.pack("apple", "banana", "orange")
print(fruits[1]) -- "apple"
print(fruits[2]) -- "banana"
print(fruits[3]) -- "orange"
This is useful if a function returns multiple values and you want to store them in an array:
local function getColor()
return 255, 150, 20, 180
end
local color = table.pack(getColor())
print(color[1]) -- 255
print(color[2]) -- 150
print(color[3]) -- 20
print(color[4]) -- 180
Then there’s the reverse table.unpack
that takes a table array and returns multiple values:
local r, g, b, a = table.unpack(color)
Again you don’t need to store everything, you may discard the values you don’t need:
local r, g = table.unpack(color)
Storing Functions in a Table
You can store functions in a table easily:
local utils = {}
utils.getColor = function()
return 255, 180, 32, 120
end
But there’s a syntax sugar we can use:
function utils.getColor()
return 255, 180, 32, 120
end
Then we can just use it normally:
print(utils.getColor())