Lua complex types

By Samir Tine, published on December 2021

Thanks for taking the time to read the first and second part of this tutorial on learning programming with Luart.
Now we'll talk about complex Lua types. Let's get started !

The table type

If you were wondering what a table in a programming language did, then you're not alone! Lua tables are similar to real tables in everyday life: on a table, you can put plates, forks, glasses, etc. In Lua, however, you will find other values: strings, numbers, booleans, and even other tables. We're not talking about furniture here, but containers. In Lua, a table is a container, which means that it's a type of value that stores other values.

Do you remember the Luart runtime library ? I had hinted at a real library (and now we know of two aisles: sys and string). These aisles are actually Lua tables, that contains functions. As you can see, tables are a simple way to store and organize values. Here is an example that shows how to create a table :

print({ "orange", "banana", "apple" }) print(sys.beep)

In this example, a table that contains 3 strings is created using braces. Lua will print to the screen table: XXXXX (where XXXX is an hexadecimal value). The content of the table will not be printed, because they are contained inside that table (remember a table is a container !).

The second line will print the beep value inside the sys table (remember the dot !), and as you already know it's a function value. Here no sound will be emitted as we don't use the parenthesis to call the beep function.

To access table content, we need to use the dot . and the name of the wanted value. That means that every value stored in a table has a "name".

Tips

Braces { }in Lua means the creation of a new table.

Lua tables are dynamic : it's possible to add or remove values inside tables at anytime. The following lines will add named values inside the sys table :

-- Let's add the christmas date to the sys table ! sys.day = 25 sys.month = 12 sys.year = 2021 sys.message = "Happy Christmas !"

In this example, we add named values to the sys table, from the Luart runtime library. A table "named value" is called a field in Lua.

Sticky note

As you can see, the equal sign = means to assign the right value to the field at the left.

In this case, we are adding 4 fields to the sys table. A more convenient way to store the Christmas date in the sys table is to use a single field like this :

sys.christmas = { 25, 12, 2021 }

The sys.christmas field will now contain a table with three number values. But..wait a minute...how to access those values ? What is the name of the field ? In this case, when no field name is provided, Lua assign an integer number that starts from 1 :

print(sys.christmas[1]) -- will print 25, the first element in the table print(sys.christmas[2]) -- will print 12, the second element in the table print(sys.christmas[3]) -- will print 2021, the third and so on...

In fact, Lua tables are very powerful containers and use a generic key/value mechanism : each value in the table is stored using a key.

Tips

There is two methods to access a table key :
  • The field notation with the dot .
  • The key notation with the brackets [ ]

A key can be any Lua value : a string, a boolean, a number, a function, even an other table, and, in fact, any other value, except nil.
To get or set a table element the universal method is to use the table[key] notation :

sys.human = { } -- sys.human is now an empty table sys.human["gender"] = "male" -- creates a new key "human" inside that table with a "male" value sys.human["age"] = 26 -- creates a new key "age" with a number value of 26 sys.human[1] = true -- creates a new key 1 (a number) with a true boolean value

When the key of a table element is a string, and that string begins with a character or a _, that key is considered as a field and only in that case you can use the field notation to access table elements :

print(sys.human) -- prints "table: XXXXX" print(sys.human.gender) -- prints "male" print(sys.human.age) -- prints "26" print(sys.human.1) -- error ! "1" is not a valid field print(sys.human[1]) -- Yes ! prints correctly "true" print(sys.human.name) -- prints "nil", the table sys.human does not contain "name" field

Please note that indexing a table (using brackets or using the dot notation) with a key that is not contained, it returns a nil value. It makes sense after all, you looked for a something that does not exist. Rather than throwing an error, Lua informs you with the nil value that the key is not valid.w

Let's go further using the nil value with tables :

sys.humain[nil] = "something" -- error ! a nil key is not a valid key ! sys.humain.gender = nil -- change the element with key "gender" from "male" to nil print(sys.human.gender) -- prints nil ! The "gender" key has been removed from the table !

Using a nil value as a key is not possible, as you may have already guessed. But assigning a nil value to a key will remove it the table. I think it's pretty well seen and logical.

On a side note, as for strings, tables can be handled with specific functions stored in the "table" aisle of the runtime Library. See the Lua 5.4 manual for a complete listing.

Well, that's all for the moment ! Just remember that tables are much more powerful than they seem with a degree of customization that you cannot yet imagine and which goes well beyond this tutorial for beginners.

The function type

You already know what a function is, just remember the print function you already know.

Sticky note

A function allows to perform an action based on arguments enclosed in parentheses.

There are plenty of functions available in the Luart runtime Library to perform various actions (create a window, choose a font, open a file, write to the console ...). Functions in Lua are values in their own right and can be used as table keys, as arguments,...

Let's see now how we can create our own function in Lua :

-- declare a function "hello" which takes no argument function hello() -- prints the message "Hello !" to the screen print("Hello !") end -- calls the hello function hello()

Functions are defined with the special word function. This special word is called a keyword. It is followed by the function name, here hello, with a pair of parenthesis ().

There's not much this function can do, so let's add some arguments to make it more useful. What will these arguments be? This brings us to a concept we have not discussed thus far: variables. In reality, a variable is simply a word attached to a Lua value. It is like a table field as we have just seen, but without the table. Here is an example:

-- declare a function "hello" which takes one argument : the variable "name" function hello(name) -- prints the string "Hello " concatenated with the value represented by the name variable print("Hello "..name) end -- calls the hello function with one argument, a string value hello("Sam")

In this example, the hello function is called with the string "Sam" as first argument. This argument is then passed to the name parameter when calling the function.

In this case, the function just prints a message to the screen, and returns nothing. In fact, functions can return one or more values. This is done with a new keyword : return, the following example demonstrates it :

-- declare a function "square" which takes one argument : the variable "number" function square(number) -- return the result of multiplying the variable "number" by itself return number*number end -- prints the value returned by the "square" function called with the argument 2 print(square(2))

This function calculate the square of a number and return its result, which is then printed to the screen. In fact, this function can be used with any other number. Function are blocks of code that we can reuse at will.

Functions are values in Lua, they can for example be assigned to a field in a table :

function add(number1, number2) return number1 + number2 end -- create a variable "mathematic" representing a new empty table mathematic = { } -- add to this table a field "addition" with the add function as a value mathematic.addition = add -- print the result of the function mathematic.addition(1, 2) print(mathematic.addition(1, 2))

Assigning a function value to a table key, makes that key callable using the same function value. In this example, the field addition contains the add function.

You may ask why field addition has a value of add and not add(). The reason is simple : when declaring a function, in fact, you declare a variable with the name of the function. Using parenthesis on that variable calls the function. Check the following example :

function add(number1, number2) return number1 + number2 end -- add is a variable containing a function value : prints "function: XXXXX" to the screen print(add) -- error !! (no arguments provided : arguments number1 and number2 are nil) print(add()) -- outputs "3" print(add(1, 2))

Functions are particularly useful. You can reuse code by using functions rather than writing the same instructions repeatedly. They also allow you to better structure your programs.

Functions in Lua have other advanced features (table method, multiple return values, anonymous functions, variable number of arguments..) that go beyond this beginner's tutorial.

This tutorial on complex types in Lua has now come to an end, since we chose not to cover userdata and thread types, which are more advanced concepts.