The Lua Tutorial
Tags: lua, programming, tutorial, learning
Published: 2024-01-05 13:00:00

## Introduction

My goal for this post is to run through the basics of the Lua scripting language. This is stuff I've learned over the past few days, and it's not the deepest explanation you'll ever see, but it's how I'm interpreting the language so I'll note it that way.

If you haven't set up an environment or don't really know what I'm talking about, I'd recommend reading my [last post](https://dev.to/jd2r/gearing-up-for-lua-3pem) that went through our main goals and installation processes.

And no, I don't fully intend for you to read this one top to bottom. It's a lot of stuff, and probably pretty dry unless you like learning languages in an hour (which I've tried, it's not fun). It's for your reference - not for a thrilling read.

That being said, it's the springboard for interesting things that I'll be making in the coming month, so it's important. Additionally, if you've ever wanted to learn Lua (or any other language I'll be playing with over the year), now's the time! I'm putting all of this material out there in the hopes that someone will find it useful - even if not today, then maybe in the future.

Without further ado, let's get into Lua.

### Basics

The `print` function outputs a line to the console. If multiple parameters are passed, it prints each one on the same line with a tab character (`\t`) as the delimiter. This function outputs any data type with a type coercion to string similar to JavaScript.

```lua
print("Hello, world!") -- "Hello, world!"
print(1, 2, 3)         -- "1    2    3"
```

You might also notice the `--` next to the intended output - that's a comment. Comments, as in any other language, aren't executed when it comes time to run the code. This makes them useful for explaining why the code works in human language without having the computer try to understand them.

Variables are relatively simple - there's no explicit type declaration, and there are only a few technical things to keep in mind when declaring and assigning them.

```lua
x = 5 -- global variable x is initialized with a value of 5

do
    local x = 10 -- local variable x is initialized with a value of 10
    print(x) -- 10
end

print(x) -- 5 (global value)
```

A variable's scope is determined by its block (the `do..end` in the code simply creates another block, similar to `{}` in JavaScript). Local variables are limited to their chunk or block, while global variables are available throughout the namespace.

I'll quickly note that Lua allows multi-variable assignment in one line, unlike some other notable languages.

```lua
a, b = 5, 4 -- a is now equal to 5, b is equal to 4
```

Now, we can talk data types - classifications for the data that variables hold. There are four basic data types in Lua - strings, numbers, Booleans, and nil.

#### Strings

- A string is an immutable sequence of characters which can be declared with single quotes, double quotes, or double brackets.
- When using single or double quotes, you can use escape characters to use quotes and other escape sequences (full list found [here](https://www.lua.org/pil/2.4.html)).

```lua
single = 'It\'s a string.'
double = "This is also a \"string\"."
bracket = [[
Here's a multiline string.
Escape characters don't work in here!
]]

print(single)
print(double)
print(bracket)

--[[

Output:

It's a string.
This is also a "string".
Here's a multiline string.
Escape characters don't work in here!

]]
```

It may also be noted that `--[[ comment ]]` is a multiline comment. This might make more sense now that double bracket strings have been introduced, because in the grand scheme of things it's just commenting out a multiline string.

#### Numbers

- A number stores a numeric value (positive, negative, and zero) in a double-precision floating-point form. Constants may include optional decimal and exponent parts.

```lua
num_1 = 3
num_2 = 1.23
num_3 = 4e+2 -- 4 * 10^2

print(num_1)
print(num_2)
print(num_3)

--[[

Output:

3
1.23
400.0

]]
```

#### Booleans

 - A Boolean value represents true or false. When other values are tested in a condition, everything except `false` and `nil` evaluate to `true`.

```lua
bool_true = true
bool_false = false

print(bool_true)
print(bool_false)

--[[

Output:

true
false

]]
```

#### Nil

 - `nil` is the absence of a value. It's assigned to global variables by default, and you can assign it to a global variable to, in essence, delete it.

```lua
nil_value = nil

print(nil_value)

--[[

Output:

nil

]]
```

As a final note, you can use `type` to get the data type of any variable.

```lua
str = "hi"
num = 4
bool = true
n = nil

print(type(str))
print(type(num))
print(type(bool))
print(type(n))

--[[

Output:

string
number
boolean
nil

]]
```

### Operators

Operators perform operations on data. Whether it's in variable form or a constant value, Lua provides several helpful operators spread among three main categories that lend needed functionality to the language.

#### Arithmetic

This is the kind that we're all familiar with: plus, minus, multiply, divide. Simple math concepts that make it possible to put that calculator logic into your programs.

We'll start this entire operator section with two variables: `num_1` and `num_2`.

```lua
num_1, num_2 = 11, 2
```

Note that we're using the multi-variable assignment pattern, mentioned earlier in our discussion of variables.

There are seven main arithmetic operators in Lua: `+`, `-`, `*`, `/`, `^` (exponentiation), `%` (modulus or remainder), and `-` (negation). Yes, the negation and subtraction operators are the same symbol - don't get them confused. One is used in a unary fashion on one variable (negation), and the other is used to combine two pieces of data (subtraction).

```lua
print(num_1 + num_2) -- 13
print(num_1 - num_2) -- 9
print(num_1 * num_2) -- 22
print(num_1 / num_2) -- 5.5
print(num_1 % num_2) -- 1
print(num_1 ^ num_2) -- 121.0 (converts to float)
print(-num_1) -- -11
```

There's also an eighth, newly added operator that does floor division or integer division (`//`). However, it's only available in versions 5.3 and up, so in an effort to be inclusive to all versions I'll just be going about the old divide-and-round-down method.

#### Relational

Relational operators compare data instead of performing an operation on it as the arithmetic operators do. They're the ones that we'll use in things like conditionals and loops, because they return a Boolean value that evaluates to `true` or `false`.

There are six of these: `==` (equal), `~=` (not equal), `<` (less than), `>` (greater than), `<=` (less than or equal to), and `>=` (greater than or equal to).

```lua
print(num_1 == num_2) -- false
print(num_1 ~= num_2) -- true
print(num_1 < num_2) -- false
print(num_1 > num_2) -- true
print(num_1 <= num_2) -- false
print(num_1 >= num_2) -- true
```

#### Logical

Logical operators work with Boolean values to give programs a structured logic. Using `and`, `or`, and `not`, we can create more complex conditional checks and combine the operators we have already for more readable code.

```lua
-- The and keyword returns the first argument if it is false and the second argument otherwise.
print(num_1 and num_2) -- 2
-- The or keyword returns the first argument if it is true and the second argument otherwise.
print(num_1 or num_2) -- 11
-- The not keyword returns the opposite (true -> false, false -> true) of the given value.
-- Any value except nil and false itself is treated as a true value.
print(not num_1) -- false
```

#### Miscellaneous

There are an additional two operators that I'll note: `..` and `#`.

The concatenation (`..`) operator combines two strings. The `#` operator gets the length of a string or table (another data structure we'll get to in a second).

```lua
str_1, str_2 = "This is one half", "and this is the other half."

print(str_1 .. " " .. str_2) -- This is one half and this is the other half.
print(#str_1) -- 16
```

### Control Structures

The first basic type of control structure is a conditional. This is the classic if-else block that many programmers will recognize from other languages. The formatting is similar to many other scripts as well, with a few differences. Here's the main form.

```lua
if condition then
    -- condition == true
elseif other_condition then
    -- other_condition == true and condition == false
else
    -- other_condition == false and condition == false
end
```

This example obviously includes all three keywords: `if`, `elseif`, and `else`. In reality, all you need is the `if` to create a conditional, but these extra are provided to give more depth. As with other languages, you can create as many `elseif` conditions as you need. Just remember to cap it with an `end` to show that the block has completed!

Here's one more example for good measure.

```lua
num_1 = 5
num_2 = 2

if num_1 > num_2 then
    print(num_1 .. " is greater than " .. num_2)
elseif num_1 < num_2 then
    print(num_1 .. " is less than " .. num_2)
else
    print(num_1 .. " is equal to " .. num_2)
end
```

Besides conditionals, Lua also offers three types of loops: `while`, `for`, and `repeat`.

The `while` loop takes a condition and loops through a set of given statements until the condition is true. It is worth noting that the statements are initially only executed if the condition is true; if it is false then they won't be executed at all and the program will continue onward.

```lua
i = 0

while (i < 10)
do
    print(i)
    i = i + 1
end -- returns 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
```

Note the `do...end` statements, creating a block of code.

The `for` loop takes a series of conditions and executes a group of statements a known number of times (given by the conditions). It's generally used for when we know the number of times that we need to iterate through, whether that's a hard-coded constant or a variable.

```lua
for j = 0, 10, 1
do
    print(j)
end -- returns 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
```

The conditions work like this: in the first argument, we pass a variable that will be manipulated by the loop structure. The second argument is a cap that ends the loop when the first initial variable gets to it. The third is the step value, which can be negative as well as positive. If you're confused, here's the [official reference page](https://www.lua.org/pil/4.3.4.html) which clears some things up.

The `repeat` loop is a `while` loop that checks at the end instead of at the beginning. It has a completely different set of use cases, but it's still aimed at situations where we don't know the exact number of iterations.

```lua
k = 0;

repeat
    print(k)
    k = k + 1

    if (k == 7) then
        break;
    end
until (k > 10) -- returns 0, 1, 2, 3, 4, 5, 6
```

You'll notice that I also made use of the `break` keyword. When used in any of these three loops, execution of the statements is immediately halted (as we can see with the example above).

### Functions

Functions in Lua are relatively straightforward. A simple function in any language has a few basic components: a name for future use, a place to include parameters from outside the function, and a way to return values from the function to the outside sphere. Lua includes all three of these in a basic function format.

```lua
function square(number)
	return number * number
end

print(square(3)) -- 9
```

Note the classic Lua form of creating a new chunk between the `function` and `end` keywords. This of course also means that `number` is undefined outside of the function, so don't try to access it.

As a side note, parameters that are not filled relapse to a value of `nil` - so keep track of your function calls to avoid any unexpected behavior!

### Tables & Iterators

There's only one advanced data type in Lua - and it's not an array, object, dictionary, or tuple. It's called a table, and it's a flexible listing option that combines the best of several advanced data types in other languages.

The #1 thing to remember when dealing with tables is that they're indexed starting at 1. If you create a table like the one below, you might be surprised at what this means.

```lua
fruits = {"Banana", "Orange", "Apple"}
print(fruits[1]) -- Banana
```

However, they also function similarly to JavaScript objects in that you can set values equal to certain keys without listing the values or keys in the original object. My favorite part of this? We can use all sorts of odd characters (even emojis!) to represent keys.

```lua
fruits = {"Banana", "Orange", "Apple"}
fruits["🥭"] = "Mango"

print(fruits[1]) -- Banana
print(fruits["🥭"]) -- Mango
```

To remove an element, use the `table.remove` method. This method has one optional parameter (the `1` below), showing which element to remove. If this isn't specified, it removes the last item.

```lua
fruits = {"Banana", "Orange", "Apple"}

print(fruits[1]) -- Banana
print(#fruits) -- 3

table.remove(fruits, 1)

print(fruits[1]) -- Orange
print(#fruits) -- 2
```

To insert an element, use the `table.insert` method. This method has one optional parameter. Usually, it'll take two: the table to alter and the value to add, but it can also take a third which is an index.

```lua
fruits = {"Banana", "Orange", "Apple"}

print(fruits[1]) -- Banana
print(#fruits) -- 3

table.insert(fruits, "Mango") -- insert Mango to the fruits table at the last index
table.insert(fruits, 1, "Papaya") -- insert Papaya to the fruits table at index 1

print(fruits[1]) -- Papaya
print(#fruits) -- 5
```

To put the entire table into a string, use `table.concat`. This method combines all of the values into a single string using an optional delimiter.

```lua
fruits = {"Banana", "Orange", "Apple"}

print(table.concat(fruits)) -- BananaOrangeApple
print(table.concat(fruits, " and ")) -- Banana and Orange and Apple
```

To sort a table, use `table.sort`.

```lua
fruits = {"Banana", "Orange", "Apple"}

print(fruits[1]) -- Banana
table.sort(fruits)
print(fruits[1]) -- Apple
```

#### Iterators

Often when we're working with tables, we want to iterate through them in order to perform some sort of action or maybe just print the whole thing accurately. For this purpose, I present the iterator.

```lua
fruits = {"Banana", "Orange", "Apple"}

for k, v in ipairs(fruits) do
    print(k, v)
end

--[[

1       Banana
2       Orange
3       Apple

]]
```

Using `ipairs`, we can quickly loop over tables. More importantly, we have the exact values of each key and value - which will come in more use when we start building bigger programs.

Alternatively, you can go the primitive route - which gets the same result. I'd encourage going with the industry standard and using `ipairs`, though.

```lua
fruits = {"Banana", "Orange", "Apple"}

for i = 1, #fruits, 1 do
    print(i, fruits[i])
end

--[[

1       Banana
2       Orange
3       Apple

]]
```

### File I/O

When dealing with reading and writing to files in Lua, we have two choices: either implicit or explicit file descriptors. They both use the same `io` library, but are accessed in different ways.

Let's look at implicit first.

```lua
file = io.open("test.txt", "a")
io.output(file)

io.write("Writing to the test file!")

io.close(file)
```

Firstly, Lua will create a file if one isn't already existing. Secondly, we open the file in append mode ("a"), and set the output to that file. In explicit descriptors, we'll skip this step. Finally, we'll write a line to the file and close it to prevent leaks.

Explicit file descriptors are only a bit different. Here's the exact same example with them implemented instead of the implicit style.

```lua
file = io.open("test.txt", "a")

file:write("Writing to the test file!")

file:close()
```

As you can see, it's much shorter because we're directly calling the methods on the file. In addition, it's easier to manage when you're reading and writing from and to multiple files at once, because you don't have to keep calling `io.output` with each different name.

I/O in Lua is a fascinating concept, which I'm hoping to explore more deeply in the future. If you're curious, here's the [official documentation](https://www.lua.org/pil/21.1.html) for the `io` library as well as more example for usage.

## Conclusion

2,900 words later, I've reached the end. But this is really only scratching the surface. My respect for this language has definitely grown - it's fun to compare it to other languages and see what's the same and what's different.

So what's after the basics? We're going to start making programs (small ones to start with). For the next week, I'll be making programs to test my newfound knowledge - I'm posting them all with the code next Friday, don't miss it.

Until then... I'm going to go take a nap. Cheers!

---

Hey there! Thanks for reading my post. If you enjoyed, leave a like and share 💖

If you're new to the series, I'm breaking down twelve popular programming languages over the year of 2024 and exploring what they have to offer as opposed to others. This is, of course, a valiant attempt to revive my programming "career" and publish interesting content.

Want to keep up with my shenanigans? Check out the GitHub [repository](https://github.com/jD2R/12in2024), which is where I'm stockpiling my projects over the year. You can also [follow](https://dev.to/jd2r) me here on DEV to get my latest posts straight to your notifications!

---