Stack OverflowLearning Lua (Fast)
[+108] [6] Stewart
[2011-11-11 10:03:10]
[ lua ]

Suppose I had to learn Lua in a hurry. What would you suggest I read and/or do?

I have some Lua scripts to fix and/or extend. Despite being a moderately competent programmer, and having a total understanding of the environment in which the Lua engine is embedded, I've never looked at Lua code.

Just to be clear, I'm looking for things like good cheat-sheets, explanations of the major gotchas in Lua, and short reference guides that get to the point without a lot of waffle or overly basic tuition on general programming.

Lua has a community that can help you learn more, met them - develCuy
[+340] [2011-11-11 17:37:26] Deco [ACCEPTED]

Rather than linking you to a generic guide, here's something that's hopefully more tailored to your needs (being a competent programmer).

First off:

Single-line comments begin with --, and multiple-line comments are surrounded by --[[ comment ]].

There are two types of variables: local and global. local variables are lexically scoped, that is:

local function abc()
    local x = 5

    local my_f = function()
        x = 7.3

    my_f() -- executes my_f

    print(x) -- prints '7.3'

abc() -- executes abc

print(x) -- print 'nil'

As seen above, the default value for an unset variable is nil (unlike in compiled languages where it is not valid to operate on an undeclared variable).

Lua is dynamically-typed, but has a very restricted set of readily available types. The types are: number, string, boolean, nil, function, table, thread, userdata, and light userdata.

Number, as seen above, stores a double precision floating point number (can represent any 32bit integer, but not every 64bit integer).

String is similar to strings in most other languages. The available literals for string values are:

s1 = "my 'text' here"
s2 = 'my \'text\' here'
s3 = [[is this three blackslashes? \\\ yes it is]]

Strings can be appended using the concatenation operator: .. , for example:

print("yo".." ".."dawg") -- prints 'yo dawg'

Strings in Lua are immutable internally. Comparing and assigning them is cheap, but concatenation and manipulation is slower. But you don't typically have to worry about the performance of this - simply don't overdo string concatenation in places where you need your code to be really fast.

From Lua's point of view, a String is simply a byte-sequence; Lua has no conception of Unicode, but you can basically just store UTF-8 encoded data inside a Lua string (and also zero-bytes).

Booleans are as expected: true or false.
The flow control keywords that are boolean dependent attempt to evaluate given values as either true or false, for example:

if s1 == s2 then print("yay!") end -- the == operator returns true or false appropriately
if s2 ~= s3 then print("cheers") end -- in Lua, ~= is the 'not equal to' operator
if s1 then print("yep") end -- all strings evaluate to true
if nil then print("uh oh") end -- nil evaluates to false

The only values that evaluate to false are nil and false itself.
The other available comparison operators are ==, ~=, <, >, <= and >=.

nil is a special type; some consider it not a type, but rather the lack of a type (and hence the lack of a value). It's entirely semantics, and should just be treated appropriately to its behavior. (It was bloody difficult to decide whether to write it as nil or nil...)

Functions are treated as values in Lua - making for an interestingly wonderful level of dynamic programming. Every file executes in an invisible 'wrapper' function. local variables declared in the file remain in the file's scope only.

Every function has an 'environment'; by default, this environment is _G. When a variable not declared in the current scope is accessed, a variable will be created in the environment, for example:

function xyz() -- this is also created in _G, due to the lack of the 'local' keyword
    x = 8

print(x) -- prints '8'

Any function that executes after this that also has its environment set to _G will be able to access this variable.
In any conflict between identically named local and global variables, the local variable will always by chosen. Similarly with any conflicts between identically named local variables available in the current scope: the deepest/closest variable available will be chosen, for example:

z = 100
local z = 22
function abc()
    local z = 6
    print(z) -- prints '6'
    print(_G.z) -- prints '100'
print(z) -- prints '22'

Note: function abc() end is simply syntactic sugar for abc = function() end.

Functions can be passed values and can return values - as expected; I would love someone to show me a viable functional language without being able to pass values directly to and from functions.
Arguments within a function are treated as local. To demonstrate the workings:

abc = function(x, y, z)
abc(1.1, 1, 7) -- prints '8.1'

This can be explained with some pseudocode:

abc = function( ARGUMENT_STACK )
abc(1.1, 1, 7)

Values can be returned using the return statement, which pushes values into the return stack; these values can be assigned to variables by the caller, or simply discarded, for example:

abc = function(x, y, z)
    return x+y*z, 22
q, w = abc(1, 2, 3)
print(q, w) -- prints '7     22'        

Lua is very accepting - it does not check the length of the argument stack (abc(1, 2)), nor does it check the types of the values.
All values of Plain Old Data types, such as number, string and boolean, are passed as anonymous copies, that is: the value is copied (efficiently) and any changes to the local argument variable will not affect the value of the original variable (unlike references in C++ or similar), for example:

local z = 8
abc = function(x)
    x = 22
print(z) -- prints '8'

The same occurs for return values.
Values of non-POD types are passed as references, that is: they hold the same value as the variable used to pass it.
The most fundamental non-POD type is table.

It is also the most versatile. A table is simply a key-value map; it takes a value as a key, hashes it, takes a value as the value, and stores appropriately. A blank table can be created with the literal {}. A table can be indexed (by key) with t[value], where t is the table, or by the syntactic sugar t.identifier which translates to t["identifier"]. Table indexes are treating by Lua as variables, allowing for easy assignment, for example:

myTable = {} -- using difference name style just to prove everything doesn't have to be lowercase! :)
myTable["abc"] = 7
print(myTable["abc"]) -- prints '7'

(I hope you don't get confused by 'value' as in "a variable's value" and "a key-value pair"!)
The key and value can hold values of ANY type. As with functions, values of POD types will be passed as copies while non-POD types are passed as references.
Yes.. that does mean you can use tables as keys. The thing to remember is that every table (and values of other non-POD types) will serialise to its own hash, determined by its location in the memory; this applies to the == and ~= operators as well.

The literals used for table creation can also be used as a form of easy initialisation, for example:

t = {
    ["xyz"] = 123,
    abc = "what",
    LaunchPlaySuperSuperFar = function() --[[ TODO ]] end,
print( -- prints '123'

There is a convenient feature with the initialising form that makes tables easy to use as arrays: if you omit the key, it will automatically assign an automatically increasing numeric key starting at 1, for example:

t = {"a", "b", "W! HAHA DIDN'T EXPECT THAT!"}
print(t[2]) -- prints 'b'
-- unfortunately t.2 doesn't work... or perhaps fortunately, it's a bit weird looking

Note that the automatic array initialising can be freely mixed with the standard form, for example:

t = {"xyz", Banana = 3, Apple = 7, "Oh no!", Orange = 1338}
--- t[1] == "xyz", t[2] == "Oh no1", t.Banana == 3, etc

This may seem confusing and slightly pointless, but it can be useful when constructing more advanced objects, such as a class system (to be discussed later).
#s gives the length of an array (does not count non-numeric keys).

Note: Function environments, such as _G, are simply tables. In Lua 5.1, you can set the environment of a function with setfenv [3] (as for Lua 5.2, I suggest you do some research), for example:

yes = "no"
t = {}
t.yes = "yes"
abc = function()
setfenv(abc, t)
abc() -- prints 'yes'

Side-note: The type of a value can be retrived in string format using type [4], for example: type(123.4) == "number"

The fact that array (actually just numerically keyed table) initialising starts at 1 leads to a very very very very VERY important point: array indexes, in Lua, start at 1 (One, Uno, Single) - NOT 0 (Zero, Zilch). If you are from a C++ background or similar, you must learn this.

Lua is an imperative language; therefore it must have flow control. The control statements available are if, else, elseif, do, while, repeat, for, for in and context-dependent statements such as return and break.
Most of the statements that contain blocks of code have their end marked by end. Despite being a flow control paradigm, functions are not really considered 'statements'; this is entirely semantics, however. The declaration of a function body is a block, and is ended with end; it's entirely up to you on how you conceptualise it.

The if statement should be the most familiar, and not just because it was introduced earlier. Its syntactic form and operating behaviour are as to be expected.

if "a" == "b" then
    print("If this prints, we have a problem!")
elseif 7 > 3 then
    print("Lua appears to have done elementary math.")
    print("Lua didn't go to school, it would seem.")

The do statement is the most basic. It simply executes code in a new scope, for example:

    local x = 7
    print(x) -- prints '7'
print(x) -- prints 'nil'

The block of code in a do statement is guaranteed to run in the location it is declared. (When I first learnt Lua, I thought it was a threaded block of code... I was wrong.)

The while statement simply executes a block of code repeatedly until the condition evaluates to false, for example:

a = 0
while a < 4 do
    a = a+1
print(a) -- prints '4'

The while statement will evaluate the condition when it is first encountered; if the condition evaluates to false immediately, the block of code will not run.

Sometimes, this is not desirable, hence there is the repeat statement.

s = "you banana you"
number_of_vowels = 0
index = 1
    if string.match(string.sub(s, index, index), "[aeiou]") then
        number_of_vowels = number_of_vowels+1
    index = index+1
until index > string.len(s) -- #s would also give the length of the string

There are two types of for loops; the first is quite basic, and is mainly syntactic sugar.

t = {"a", "b", "c"}
    local i = 1
    while i <= #t do


        i = i+1

This can be compressed into:

t = {"a", "b", "c"}

for i = 1, #t, 1 do

The format is for variable = minimum, inclusive_maximum, step, where interval is how much to add to the variable each time (can be non-integer and/or negative.. just remember to flip the minimum and maximum). Wrapping the while loop above in a do block is required as the variable will be local to the loop; it will not use any local or global variable available in its scope.

The format of the second type is as follows:

for var1, var2, var3, ... in iterator_function, arg1, arg2, arg3, ... do
    -- code

This type can be difficult to understand - in fact, its workings may be considered as 'internal' which is not what you wished to discuss.
A simplified explanation:

iterator_function is called with its arguments and it returns var1, var2 and var3 the code is run with the values of var1, var2, and var3 iterator_function is called with var1, var2 and var3 as its arguments, and returns var1, var2 and var3 the code is run with the values of var1, var2 and var3 rinse and repeat until the value of var1 is nil (may be first time, in which block will never run)

To make this significantly easier, Lua provides pairs [5] and ipairs [6]; these provide iterator_functions and their arguments for iterating over tables, for example:

t = {a = 1, b = 2, c = 22}
for k, v in pairs(t) do
    print(k.." is "..v)
--[[ Result will be:
       a is 1
       b is 2
       c is 22

ipairs is for operating over array tables.
Note:: In the standard Lua implementation, ipairs is extremely inefficient compared to for i = 1, #t do. I don't have the research available, but I know it to be true.

In any of the loop-based flow control statements above, the break statement can be used to cease the execution of the deepest loop and continue execution.

The return statement acts in a similar way. It returns from the current function.

Lua has boolean logic.. but like how values can evaluate to booleans, boolean logic can evaluate to values, for example:

y = true
x = y and 5 or -7
print(x) -- prints '5'

This is the same as:

y = true
if y then
    x = 5
    x = -7
print(x) -- prints '5'

If you are familiar with a language that uses the condition ? valueA : valueB syntax, then an understanding of this should come easier to you.

You can extend the statements, for example:

y = false
z = false
x = y and 1 or z and 2 or 3
print(x) -- prints '3'
z = true
x = y and 1 or z and 2 or 3
print(x) -- prints '2'

This allows for massive compound chaining of logic.
Unfortunately, this may quick become an incomprehensible mess of ands, ors and poor indentation; I find the following indentation style is helpful:

local colour = (
        string.match(str, "^pale") and (
                string.match(str, "red$") and Colour(255, 120, 120)
            or  string.match(str, "blue$") and Colour(120, 120, 255)
            or  Colour(200, 200, 200)
    or  string.match(str, "^dark") and (
                string.match(str, "red$") and Colour(120, 0, 0)
            or  string.match(str, "blue$") and Colour(0, 0, 120)
    or  str == "red" and Colour(255, 0, 0)
    or  str == "blue" and Colour(0, 0, 255)
    or  str == "white" and Colour(255, 255, 255)
    or  error("Unknown colour!")

(Just an example.. I'm sure the functionality could be much better implemented)

Error messages in Lua are quite helpful. By default, they provide you with a descriptive error message, a line number, and a stack traceback (:breadcrumb trail of function calls).

test.lua:43: 'end' expected (to close 'function' at line 29) near '<eof>'

This particular error message is possibly the most helpful ever; when I discovered it I thought "Genius!". (My head practically exploded when I tried Eclipse IDE).

As they say "Keep your code close and your error messages closer" - learn them and you'll find the path of Lua much nicer to tread.

For practical use of Lua, you need to be aware of the standard functions; a full list of the functions available in the standard Lua 5.1 libraries can be found in the Lua 5.1 manual [7].

It would be extremely tedious to document a set of important functions you may find useful, even if a small subset and the information brief.

I found a script I made in less than 20 minutes for a challenge. It identifies statistics on a set of names regarding how easy they are to type on standard 0-9 keyed phones (with "abc", "def", etc). It's available here [8] (see bottom for results).

I apologise about the poor sample quality... it should still be useful it you look at the functions I use to perform certain operations.

You should do some research into additional libraries and batteries-included packages, such as Penlight [9] and stdlib [10]. I suggest you take a look at Lua for Windows [11]; it has an extensive range of commonly known libraries, from GUI and IO libraries to XML parsers and IDEs. It's quite helpful for someone starting the language.

As for object orientation, it's mostly up to you; the dynamic model that Lua uses allows for you to use tables to represent very complex objects.

To assist with this, Lua provides metatables and userdata. I won't go into depth here, but they very powerful and provide the means to implement every type of object orientation model imaginable, albeit less efficiently than with a specially design language.

I find metatables to be a very empowering tool, and I would not use Lua if it lacked them. If you are looking to use Lua for serious development, I suggest you look into them.

I want to include more information about them in this introduction, but I lack the time and effort (perhaps someone else could expand on them, please).

In my programming days, I've found Lua most useful. It's an incredible language that can be applied to any situation.

Depending on what you wish to use Lua for, you'll find it leads you on a different path.
Wherever it takes you, I hope this introduction was a good start for your future endeavors.

Good luck in the land of Lua! :)


(4) I haven't proofread this at all. If you see a mistake, fix it please! :) - Deco
(70) +1 for the effort - jpjacobs
(4) wow, very informative! +1 - Zhanger
(8) If I could +10 I would. Great effort. - Undo
I am missing the multiple value return behaviour described in [], where the number of returned values gets adjusted. To me, that reminds me of the CONS function in lisp languages. - comonad
(1) What an excellent answer! - alistairholt
(1) don't know about bookmarking the reference manual, but bookmarking this definitely! - Ravi Upadhyay
(1) This is one of the most useful answers I have ever seen on StackOverflow, really, the entire internet. - User.1
Thank you so much for this incredibly detailed answer. I have no idea why this question was closed... - Con Antonakos
[+13] [2011-11-11 13:47:11] Michal Kottman

If you are looking for a simple, compressed list (cheat sheet) of what is available in Lua 5.1, take a look at the Lua Short Reference [1].

It really is short (4 pages printed), but apart from some other languages, it is a complete reference to the language, i.e. syntax, semantics and base library are all explained in the 4 pages. This is what I like about Lua :)

There are also a bunch of tutorials on Tutorial Dictionary [2].


[+10] [2011-11-11 10:54:41] ponzao

I personally find Lua Unofficial FAQ [1] very useful for those gotcha questions.


[+6] [2011-11-11 13:58:00] kikito

If you are the learning-while-programming type, then you can have a look at the Lua Missions [1] (a.k.a. Lua Koans).

Disclaimer: I'm the Lua Missions author.


[+3] [2011-11-11 10:07:27] Joachim Pileborg

Spend a day or two experimenting, then do the assignment with one eye on the reference. Usually works for me. :)

[+3] [2011-11-11 12:47:04] christosc

I myself got fairly up to speed on Lua within roughly a month by skimming through "Programming in Lua" (PiL) textbook, 2nd ed., looking for quick reference on anything needed in the Lua Reference Manual, and by experimenting in the Lua REPL (having first aliased it to rlwrap lua). I used the hard copy form of the books mentioned, because it suits me better, but the 1st edition of PiL and the Reference Manual are also available online here [1] and here [2].