Please login

Get started with Lua (1/3)

The Lua language

CodeFlow uses Lua for live-coding iOS, tvOS, and MacOS applications. And if you are an  app developer, you are probably much more familiar with Objective-C and Swift than with the Lua language. If this is the case, no worry, this article has been especially written for you.It will give you an overview of the Lua language and highlight the features that make Lua a great language, although quite different from the compiled languages generally used on our platforms.

The goal is that, after reading it, you feel comfortable enough with Lua to start coding right away.

There are several ways to use the materials in this article: first, you can probably get a quick overview of Lua by reading it sequentially, even if some details in the provided code examples remain unclear at first. Then you can use it as a quick Lua reference, and use the table of content (on the left) to jump directly to a specific topic, on which you need details or information. Finally, as all provided code examples are valid Lua code, you can copy any of them in CodeFlow, execute it in step-by-step to get a full understanding of what it does, and modify it to try additional stuff.

The second part of this article (available soon) will present the main CodeFlow-specific additions to Lua, that permit live-coding in your app, and enable the interaction between your Lua code and the underlying native SDK. If you are already familiar with the Lua language, you can jump directly to this second part.

Part 1 - The Lua language

This goal of this section is to give you a first overview of the Lua language, without entering too much into the details, so you should be capable, after reading this, to understand any reasonably simple Lua code and to write your own Lua code. Therefore, for the sake of simplicity, some advanced features of the language will be intentionally omitted here.

If you already have downloaded CodeFlow on your computer, you can try the code samples given here in a local Lua document.

A first taste of Lua

Let's get started with this simple Lua code sample:

-- This is a Lua comment
local n = 8
local t = {} -- t is a Lua table
-- Define a function
function cube (x)
return x^3
end
-- Fill t with the n first cubes
for i = 1, n do
t[i] = cube(i)
end
-- Return the cubes table
return t

This code sequence:

starts with a comment line,

declares two local variables n and t, and initialize them with some values: nwith the number 8, t with an empty Lua table (i.e. an associative array).

Then it declares a function cube that returns the cube of its parameter x,

it runs a for loop that fills table t with the cubes of the n first integers,

and finally it returns the table t to an eventual caller.

Nothing too complex so far, huh?

Notice that Lua doesn't require semicolons (or any other separator) at the end of statements. Semicolons are however accepted as a way to indicate a separation between statements, but they are rarely needed. In addition, Lua considers newlines and whitespace characters as equivalent, so you can layout your code as you want.

Data Types

Lua has a small set of data types:

Number: a numeric value like 42 or3.14159. In Lua 5.2, the version currently used in CodeFlow, all numbers are 64-bits floating point numbers. But don't freak out, 64-bit doubles have 52 bits for storing exact int values, so this is generally not an issue.

Boolean: true or false.

Nil: nil has the usual meaning of representing the absence of value.

String: Strings in Lua are immutable. Simple literal strings can be put into single or double quotes, so 'abc' and "abc" are equivalent. For defining multiline strings, you enclose them between long brackets, typically [[ and ]].

s = "A simple string"
s2 = [[
A long text
that extends
on several lines]]
s3 = [=[A multi-line string can also be enclosed
between long brackets with additional '=' characters
if it conttains a ]] pattern!]=]

Table: it is the data structure in Lua. As already mentioned, a Lua table is basically an associative array. We will discuss tables in more detail later in this document.

Function: functions are first-class values in Lua and they will also have their own dedicated paragraph later.

Thread and Userdata are advanced types, so we won't discuss them here.

Comments

In Lua, there are two types of comments:

line comments start with -- and extend up to the end of the current line;

block comments have a structure similar to multi-line strings: they are enclosed between patterns like --[[ and ]], or --[===[and ]===].

Having variable opening and closing patterns for block comments has the great advantage that block comments can easily be nested, provided inner block comments have non-conflicting closing patterns, as shown in the example below:

Note: CodeFlow takes care of nesting comments for you, so that commenting / uncommenting a block of code just behave like you expect.

Variables

Variable in Lua have the usual meaning: a place where you store values (or more priecisely references to values). Lua variables are not typed, i.e. a variable can store a value of any type. Variable names can be any combination of letters, digits and underscore (not starting with a digit).

Lua has two kinds of variables: global variables and local variables.

Local variables

Local variables are declared with the keyword local.

local myVariableWithALongName -- this variable is initialized to nil
local a, b, c = 1, 'this', 'that' -- You can declare and initialize several variables on a single line
local _ -- also a valid variable name

Local variables have a precise scope: a local variable is visible from its definition up to the end of the current code block:

local x = 1
local someCondition = true
if someCondition then
local y = x -- y is local to the 'if' block, x is visible inside enclosed blocks
y = y + 2
x = math.sqrt(y) -- square root from the math standard library
end
-- y doesn't exist here anymore
-- but x does exist because we are still in its declaration block
print (x) --> 1.7320508075689 (√3)

Lua is flexible regarding local variable names: it is okay in Lua to declare a local variable with the same name as a previously declared variable in the same block or in an enclosing block. In this case, the new variable simply masks the previously defined one:

local x = 1
do
local x = 3.5
-- from here to the end of this do..end block, x is the new declared variable
x = x - 1 -- x value is now 2.5
end
print(x) --> 1 (we are back to the local x declared on line 1)
local function getFirstX () -- Yes, getFirstX is a local variable containing a function!
return x -- x here is a reference to the local variable x in the enclosing block
end
local function incrementX (incr) -- function parameters are local variables as well, visible inside the function block
x = x + incr -- the value of the local variable x in the enclosing block is changed
end
local x = 'hello' -- declares a new local variable name 'x' that masks the previous x, but does not remove it
print(x) --> hello
print(getFirstX()) --> 1 (the value of the variable x declared on line 1)
incrementX(2)
print(getFirstX()) --> 3 (variable x declared on line 1 has be incremented)
print(x) --> hello (the current variable x is still the one containing 'hello')

In the daily programming, variable masking is very handy for cutting and pasting blocks of code containing local variable definitions, but be aware that a local variable declaration can have unexpected effects on variables with the same name located after the declaration, in the current code block. :)

CodeFlow include special features for finely controlling the scope of Lua variables: for example, you can double-click on a variable name to highlight all uses of this variable in the code, in a perfectly Lua-syntax-compliant way.

Global variables

Global variables, also called free names, are not expressly declared in Lua. Actually Lua considers as a global variable any variable identifier that does not correspond to a local variable defined in the current block or in one of its enclosing block.

Global variables have a global scope: once set, a global variable is accessible from anywhere in your program. A never-set global variable has the value nil, and you can remove a global variable by setting it to nil again.

i = 42 -- assigns 42 o the global variable i
-- now i is available from everywhere in your program, except where a local variable with the same name is defined
print(i) --> 42
print(j) --> nil (j is a not-yet-set global variable)

Note: actually the set of global variables visible at any given point of a Lua program can be finely control via the use of Lua _ENV (environment) tables, but to keep things simple in this quick overview, we will consider for now that global variables are really … global.

Good practice

So which ones shall you use in your program? Local variables or global variables?

Like in most other programming languages, the good practice in Lua is to use local variables as much as possible, and to reserve global variables for standard libraries, except in rare cases where your program really needs to store some of its state at a global level.

In addition, local variables are much faster than global variables, so using local variables is also good for your program's performance.

Warning: because they are implicitly declared, it is a rather common mistake when writing Lua code, to accidentally define a global variable by mistyping a variable name. To help you detect easily this kind of error, CodeFlow displays local and global variables with different colors in the source code.

Operators and Expressions

Lua supports the classic set of arithmetic operators+ - * / %, plus a less common exponentiation operator ^:

Comparison operators are the classic ==, >, <, >=, <=, and the less-classic non-equal operator ~= (tilde-equal). Their result is always a boolean value trueor false.

Logical operators in Lua are and, or, and not. Logical operators, like condition evaluation in control blocks, consider both false and nil as false, and anything else as true.Note that this means in particular that 0 is considered as true by logical operators and blocks conditions, which you may find surprising at first, if you are used to languages of the C family!

and and or are shortcut operators: they return their first operand if false-or-nil (and) or non-false-nor-nil (or), without evaluating their second operand; otherwise, they evaluate and return their second operand.

local cond1 = not true --> false
cond1 = not 0 --> false (zero is considered as true)
cond1 = not nil --> true (nil is considered as false)
local myVar = cond1 and 'hello' or 'world' --> hello
cond1 = not cond1 --> false
myVar = cond1 and 'hello' or 'world' --> world
function f() return 123 end
myVar = cond1 and f() or 'world' --> world (function f is not called here)

Other operators include the string concatenation operator .. and the length operator #.

Lua doesn't have compound assignment operators (like +=), as those generally don't fit well with multiple assignment. This is not really annoying, especially if your code editor has a good code-completion feature.

Conditional execution

The if statement in Lua has a familiar structure:

if --[[condition1]] then -- starts with a 'if' block,
-- statements
elseif --[[condition2]] then -- optionally followed by one or more 'elseif' blocks,
-- statements
else -- finally it can have an optional 'else' block
-- statements
end -- and is always terminated by a 'end' keyword.

Examples:

local a = -1
-- if control block (simple version)
if a > 0 then
print "a is positive"
end
-- if control block (full version, with one elseif block and an else block)
if a > 0 then
print "a is positive"
elseif a < 0 then
print "a is negative"
else
print "a is null"
end

Note: by design choice, Lua doesn't include an equivalent of the switch statement found in languages of the C-family. Therefore, to write the equivalent of a switch statement in Lua, you generally use an if with an elseif block for each case, and the else block for processing the default case.

Loops

There are four types of loops in Lua: while loops, repeat ... until loops, and two kinds of forloops. Their structure is quite simple, as you can see in this exemple:

local a = -1
-- while statement
while a < 10 do
a = a + 1
end
-- repeat until statement
repeat
a = a - 1
local b = 2 * a
until b == 2 -- You can use in the end condition a local variable declared inside the repeat until block
-- numeric for statement
for i = 1, 10 do
-- i is a local variable whose scope is the for loop's internal block
-- and whose value varies from 1 to 10 inclusive, with an (implicit) step equals to 1
a = a + i
print(a)
end
-- generic for statement
for key, value in pairs(math) do
-- iterates through the 'math' table, using an iterator ('pair' is a standard table iterator)
if key == 'pi' then
print ("Found pi!", value)
break -- exits the for loop
end
end

A break statement can be used to terminates the execution of a while, repeat, or for loop, skipping to the next statement after the loop.

Tables

Tables are the only data structure type provided natively by the Lua language.

In addition Lua provides an extension mechanism, named userdata, that allows to easily add new data types to the language. This extension mechanism is used extensively by CodeFlow for providing access to native Platform APIs, in a way fully consistent with the Lua table syntax. For this reason, having a good understanding of the table syntax in Lua is key for all CodeFlow users.

A Lua table is essentially an associative array, i.e. a collection of (key → value) pairs, in which each key is unique. Keys and values can be of any type, except nil.

Lua table is a very flexible data type. A Lua table can be used as a dictionary, as an array when keys are integers, or as a set (typically by filling the table with a collection of (key → true) pairs).

Like the rest of the Lua language, the syntax for using tables is simple and easy.

-- build a simple sequence
local primeNumbers = { 2, 3, 5, 7, 11, 13 } -- create an array-like table containing the given values
-- access to elements
print (primeNumbers [1]) --> 2 (table indexes start at one)
print (primeNumbers [5]) --> 11
print (primeNumbers [8]) --> nil (no element at index 8)
-- Change the table
primeNumbers [8] = 19 -- set entry at index (i.e. for key) 8
print (primeNumbers [8]) --> 19
print (primeNumbers [7]) --> nil (tables used as a sequence can have holes)
primeNumbers [7] = 17 -- fill the hole
-- you can iterate though a sequence using the 'ipairs' iterator: for index, value in ipairs(table) do ... end
-- (indexes are enumerated in order and enumeration stops at the first index without value)
for i, p in ipairs(primeNumbers) do
print(string.format("prime [%d] is %d", i, p)) --> prime [1] is 2, prime [2] is 3...
end
-- create a set of the first prime numbers
local primesSet = {} -- initialize primesSet as an empty table
for _, p in ipairs(primeNumbers) do -- we don't care about the index here, so we name it '_'
primesSet [p] = true
end
print (primesSet[3]) --> true (key 3 is in the set)
print (primesSet[8]) --> nil (key 8 is not in the set)
for i = 1, 20 do
if primesSet[i] then -- test if key i is in the set
print (string.format("%d is a prime number", i)) --> 2 is a prime number...
end
end

Naturally, tables can also be used to store structured data:

-- create and fill a 'cat' structure
local cat = {}
cat ['legs'] = 4 -- set key 'legs' with value 4
cat.isCarnivore = true -- shorter notation for string keys, equivalent to cat['isCarnivore'] = true
cat.family = "Felinae"
-- create a 'duck' structure as a table litteral (using the generic notation)
local duck = { ["legs"] = 2, ["isCarnivore"] = false, ["family"] = "Anatidae" }
-- create a 'ant' structure as a table litteral, using the shorter notation for string keys
local ant = { legs = 6, isCarnivore = false, family = "Formicidae" }
-- store the previous variables in a table
local animals = { ["cat"] = cat, duck = duck } -- duck = duck?? 'duck' on the left of the equal sign is a string key, the one on the right is the duck variable defined above
animals.ant = ant
animals.lion = { legs = 4, isCarnivore = true, familly = "Felidae" } -- use a table literal as the value
-- access to table fields
print (animals.cat.isCarnivore) --> true
animals.cat.category = "Mammal" -- sets a new key "category" in animals.cat with value "Mammal"
-- variables hold references to table; an assignment copies the reference, not the table
local cat2 = animals.cat
cat2.name = 'Felix'
print (cat.name) --> Felix (cat, cat2, and animals.cat are references to the same table)
-- By default, two tables are equal only if are the same object, not if they have the same contents
print (cat == cat2) --> true
local ant2 = { legs = 6, isCarnivore = false, family = "Formicidae" } -- same contents as variable ant above
print (ant == ant2) --> false
-- table keys are not limited to strings or integers, but can be anything except nil
local population = { [ant] = 'huge', [animals.lion] = 'small' } -- a table whose keys are tables!
population [cat] = 'large'
print (population[animals.ant]) --> huge (animals.ant is a reference to the same table as the ant variable)
-- you can enumerate a table using the 'pairs' iterator: for key, value in pairs(table) do ... end
for name, animalInfo in pairs(animals) do
if animalInfo.isCarnivore then
print(string.format("%ss are carnivore", name)) --> lions are carnivore
--> cats are carnivore
end
end

In addition to the basic features presented above, Lua tables are highly customizable, thanks to a mechanism named metatables. But discussing Lua metatables would be out of the scope of this getting started article.

Functions and methods

We have already met simple functions here and there, in our quick tour of Lua, but without really explaining how functions are defined and used in Lua. Now the time has come to explore them in more detail.

Functions are first-class values

Lua functions are first-class values. This mean that you can do with function values everything you do with other types of value: store them in variables or in tables, compare them with other values, use them in expressions, or pass them as function parameters. And in addition, you can of course call them.

What it also means is that Lua functions don't intrinsically have a name: the name used for calling a function is the name of the variable in which it is stored.

Functions are closure

Lua functions are closures: they can capture and modify local variables accessible from the context in which they are defined. In this regard, Lua functions are similar to Objective-C blocks, Swift closures, or C++ lambdas.

In Lua, external local variables used by a function are named upvalues. Lua automatically detects upvalues used in functions and make them available in read and write mode when the code is executed, so you don't have to do anything special when using them.

Functions have flexible parameters

Not surprisingly, a Lua function definition contains a list of parameters, right before the function body. These parameters act as local variables inside the function, and they are initialized with the argument values passed by the caller, or with nil if the caller doesn't provide enough arguments.

Methods are table functions

Lua tables can store values of any type, and in particular they can store function values. Storing functions in tables can have various purposes. A table can be used to group a set of related functions, with keys being the names used to access these functions. For example, Lua standard libraries follow this pattern, and the math library is a table containing functions like sin, cos, tan…

Another common reason of storing functions in a table is for object-oriented-programming (OOP). In that case, functions are put in the table to implement the behavior of the object represented by this table. Therefore, they need to have access to the table, generally provided as a parameter. Such functions stored in a table and taking this table as a parameter, are methods.

Lua provides a specific syntax for defining and calling methods, as shown in this code sample:

-- create a table
local counter = { count = 0 }
-- add a function, with a table parameter, as a field of this table,
counter.increment = function (t)
t.count = t.count + 1
end
-- this function definition syntax is equivalent to: counter.decrement = function(t) ... end
function counter.decrement (t)
t.count = t.count - 1
end
-- let's call the counter's functions on the counter itself
counter.increment(counter) --> counter.count: 1
counter.increment(counter) --> counter.count: 2
-- using the method notation would do the same in a simpler and more elegant way
counter:increment() --> counter.count: 3 (notice the ':' here)
counter:decrement() --> counter.count: 2
-- the ':' notation can also be used when declaring a method; it adds an implicit first parameter 'self' to the function
function counter:setValue(newCount)
self.count = newCount -- 'self' refers to the current table or object
end
-- call this method
counter:setValue (100) --> counter.count: 100

In summary:

you call a method with the method syntax expression:methodName(param1, param2, ...), which is equivalent to calling it as expression.methodName(expression, param1, param2, ...), with the small (but sometimes significant) difference that with the method notation, the object reference expression is evaluated only once.

and you define a method using the syntax:

function expression:methodName(param1, param2, ...)
-- self is an implicit parameter, referencing the current object
-- ...
end
-- equivalent to the following table function definition syntax:
function expression.methodName(self, param1, param2, ...)
-- self, the reference to the current object, shall be the first parameter
-- ...
end

Code chunks and modules

Lua chunks are functions

Lua calls a chunk any Lua source file or string passed to the Lua compiler. A Lua code chunk is compiled as an anonymous function, and what you write in the Lua source file or code string is simply the body of this function.

As a function body, a Lua chunk can define local variables and can return values. In addition a chunk has a (generally unused) vararg parameter and a single upvalue _ENV, that by default gives access to Lua global variables.

All code samples in this article are valid Lua chunks, so you can run them in CodeFlow or in any other Lua-compatible environment.

In addition, here is an example of a Lua chunk with a return statement, intended to be called from another Lua chunk:

Lua code execution

Executing a Lua chunk is a two-step process: first the Lua compiler transforms the chunk's source code into a function, then the resulting function can be executed.

Lua provides standard library functions, like load and loadfile, that compile a string or file into a function. Let's see how to use them in an example:

-- Get a function for executing a Lua source code string
local sayHello = load ([[print "Hello World"]]) -- using long brackets delimiters for this string avoids to worry about internal single- or double-quotes
-- call this function to execute the source code string
sayHello() --> Hello World
sayHello() --> Hello World
-- Suppose that the counter code chunk from the previous example is stored in a file named "counter.lua"
local counterChunkFunction = loadfile("counter.lua")
-- counterChunkFunction is a Lua function whose body is the top-level block in the file "counter.lua"
-- this function creates a counter and returns it
local counter1 = counterChunkFunction() -- create a counter
local counter2 = counterChunkFunction() -- create an other counter
counter1:increment (3) -- counter1.count --> 3
counter1:increment () -- counter1.count --> 4
counter2:decrement() -- counter2.count --> -1

Lua Modules and the require function

A Lua module is basically a code chunk that you want to execute only once, even if it is used in various places of your program. Such a run-only-once behavior is generally desired when importing libraries or object class definitions. In Lua, you load modules by calling the require function.

require takes a single parameter: the name of the required module. If the given module has not been loaded yet, require compiles it, executes the module's chunk function, and returns the result of this function. On the other hand, if the given module has already been loaded, require simply returns the memorized result of the module's chunk function, without calling it again.

To make this clear, let's see how our counter chunk behaves, when loaded as a module using require:

-- Here the counter code chunk from the example above is stored in a file named "counter.lua", at a location suitable for the require function
local counter1 = require "counter" -- the module name does not include the '.lua' extension
-- require returns the result of the code chunk's function (here a counter table)
counter1:increment (3) -- counter1.count --> 3
counter1:increment () -- counter1.count --> 4
-- require "counter" always return the result of the chunk's function first run
local counter2 = require "counter" -- the 'counter' code chunk is not executed the second time
counter2:decrement() -- counter2.count --> 3
print (counter1 == counter2) --> true

The rules that require uses to find a module by knowing its name won't be explained here, because they are quite different for CodeFlow and for the standard Lua implementation. Actually CodeFlow implements project-based module search, better suited for app development, where standard Lua has a more traditional approach, based on known file paths and dynamic libraries.

Going further

At this point, you should have a quite good understanding of the Lua language, except for a few advanced features. If you were already familiar with Lua, you might still have learned one thing, or two.

So, where to go from here?

Read about CodeFlow additions to Lua

If you are interested in CodeFlow, the two other articles in this Get started with Lua series are
highly recommended readings.

The next article, Get started with Lua - CodeFlow additions to Lua, presents a few significant additions that CodeFlow brings to Lua. The main of these additions is the CodeFlow object framework, a nice and powerful object model integrated right into Lua. The CodeFlow object framework has a key role in CodeFlow, as it enables dynamic code update and native objects bridging. And, as you will learn in this article, it is also very easy to use! 😎

The third article of this series, Get started with Lua - CodeFlow native bridge, is an indispensable overview of the native bridge, the software layer allowing you to transparently mix Lua and native code in your application. In this article, you will learn how to use native objects in your Lua code, how you can use C structs, enums, and most other types in Lua, and how easy it is to make your Lua objects visible (and callable) from the native code.

Learn more about Lua

Of course, this article does not intend to be a complete Lua course, and to keep it at a reasonable length and level of complexity, I chose to omit advanced features of the language.

Among these advanced features, probably the most important are the customization capabilities that are an integral part of the Lua language.
lua.org says about these:

Lua is powerful (but simple):
A fundamental concept in the design of Lua is to provide meta-mechanisms for implementing features, instead of providing a host of features directly in the language. For example, although Lua is not a pure object-oriented language, it does provide meta-mechanisms for implementing classes and inheritance. Lua's meta-mechanisms bring an economy of concepts and keep the language small, while allowing the semantics to be extended in unconventional ways.

Beside customization, a few other interesting features of the Lua language have omitted in this article, and may be the subject of a future advanced Lua article: