Juraj's Blog

07 Feb 2021

Notes from Programming in Lua

See the source book Programming in Lua targeted at Lua 5.0.

Here are my notes in case I need to come back to it. I’ll mostly note the differences to Python as that’s what I’m used to these days.


Every piece of code is a chunk Semicolons may optionally follow statements

This is valid, but ugly:

 a = 1   b = a*2

Global variables

Don’t need declaration, just assign a value to create it.

print(b)  --> nil
b = 10
print(b)  --> 10

Lexical conventions



A-Z a-z 0-9 _, must not start with a digit


Single line: -- double hypen until the end of line Multi line: --[[ until a matching `]]


Eight basic types in Lua: nil, boolean, number, string, userdata, function, thread and table


Strings in double quotes: “a string”

Literal strings can be defined with double square brackets [[ … ]] - also multiline

automatic conversions between numbers and strings Any numeric operation applied to a string tries to convert the string to a number:

print("10" + 1)           --> 11
print("10 + 1")           --> 10 + 1
print("hello" + 1)        -- ERROR (cannot convert "hello")

Conversely, whenever it finds a number where it expects a string, Lua converts the number to a string:

print(10 .. 20)        --> 1020

A comparison like 10 == “10” is always false, because 10 is a number and “10” is a string. If you need to convert a string to a number explicitly, you can use the function tonumber, which returns nil if the string does not denote a proper number.


Double-precision floating point numbers, no integer type


Table type: {} Associative array

table fields evaluate to nil if not initialized

Dump table:

See https://stackoverflow.com/a/27028488

function dump(o)
   if type(o) == 'table' then
      local s = '{ '
      for k,v in pairs(o) do
         if type(k) ~= 'number' then k = '"'..k..'"' end
         s = s .. '['..k..'] = ' .. dump(v) .. ','
      return s .. '} '
      return tostring(o)

Because numbers and strings are different types, t[0] and t[“0”] are different locations


Arbitrary C data, mostly used with C API


Not first-class, typically implemented indexing tables with integers.
Customary to start arrays with index 1

Can be initialized as

squares = {1,4,9,16,25}
-> { [1] = 1,[2] = 4,[3] = 9,[4] = 16,[5] = 25}


first-class values - can be stored in variables, passed as arguments, etc


Arithmetic operators

Relational operators

< > <= >= == ~=

(!) inequality operator is ~=

alphabetical order for strings: "2" > "15"

Logical operators

and or not false and nil are false, everything else is true


String concatenation: .. Auto-converts numbers to strings Cannot concatenate booleans or nils


See https://www.lua.org/pil/3.5.html

Table constructors

Can initialize arrays (sequences/lists) by passing values (list-style constructor)

days = {"Monday", "Tuesday", "Wednesday"}
print(days[2]) --> Tuesday

Can initialize with keys (record-style constructor):

w = {x=0, y=0, label="console"}
--> { ["y"] = 0,["label"] = console,["x"] = 0,}

We can use tables to implement linked lists

list = nil
for line in io.lines() do
    list = {next=list, value=line}

Trailing comma is optional - a = {2,3,4,}


Multiple assignment

x = 3
a, b = 10, 2*x

Lua adjusts number of values to the number of variables - extra variables receive nil, extra values get discarded

Multiple assignments are useful to return multiple values from a function

a, b = f()

Local variables and blocks

    x = 10
    local i = 1        -- local to the chunk
    while i<=x do
      local x = i*2    -- local to the while body
      print(x)         --> 2, 4, 6, 8, ...
      i = i + 1
    if i > 20 then
      local x          -- local to the "then" body
      x = 20
      print(x + 2)
      print(x)         --> 10  (the global one)
    print(x)           --> 10  (the global one)

It is good programming style to use local variables whenever possible.

Local variables have the scope limited to the block where they are declared.

Local variables can be initialized with a global variable:

local foo = foo --local foo initialized with the value of global foo

Blocks can be explicitly delimited with doend

Control structures

if while repeat for

All have explicit terminators

  • ifend
  • forend
  • whileend
  • repeatuntil
while exp do block end
repeat block until exp
if exp then block {elseif exp then block} [else block] end

repeatuntil similar to dowhile in C (executed at least once)


Numeric for vs generic for


for var=from,to,increment do

to is inclusive: step defaults to 1

Generic for Traverse all values returned by iterator function

days = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
for i,v in ipairs(days) do print(v) end --> Values
for i,v in ipairs(days) do print(i) end --> 1,2,3,4,5,6,7

Standard libraries provide iterators - line of files, pairs in table, words of a string



-- add all elements of array `a'
function add (a)
    local sum = 0
    for i,v in ipairs(a) do
    sum = sum + v
    return sum

Multiple results

See also table.unpack that returns all elements from an array

f = string.find
a = {"hello", "ll"}
--is the same as
string.find("hello", "ll")

Variable number of arguments

Define parameter list as .... When the function is called, arguments collected in a single table named {...}.

function sum(...)
  local total = 0
  for i,v in ipairs({...}) do
    total = total + v
  return total

Named arguments

Parameter passing is positional, but we can achieve this with tables as parameters:

function rename (arg)
    return os.rename(arg.old, arg.new)
rename{old="temp.lua", new="temp1.lua"}


When a fuciton is enclosed in another function, it has full access to local variables from the enclosing function (lexical scoping)

Package management

LuaRocks - usage:

luarocks install luafilesystem --local


Set of utility modules - input data handling, functional programming, OS path management: https://github.com/lunarmodules/Penlight