Lua for the Python programmer

1. Introduction

This is primarily for myself to finally get over the hesitation about Lua and learn useful things about it. One way to do that is to compare and contrast with another languages in the same 'space' and one I'm more familiar with -- Python.

Python and Lua:

NOTE: I'm not going to cover "why not" aspects. If you don't want to use Lua, you will find many reasons, I'm sure ;)

2. Hello World

{hello.lua 2}
-- Single line comments in Lua start with two dashes: --
-- what I like about the dashes is that I don't have to use shift key
-- as I have to in Python to type the # character.

-- the print statement is familiar to Python(3) programmers
print("Hello, World!")

-- you can also use the single quote character for strings
-- which I prefer anyway
print('Hello, again!')

-- multi line strings are bookended by two square brackets
message = [[Dear Python Programmer,
Lua is a small, fast, and handy language
that is often embedded in larger programs, like
video games ...]]
{datatypes.lua 2}
-- All n umbers are internally represented as 64 bit double
-- of which 52 bits are for numeric representation

x = 42 -- this declares a global variable x
print(x)

-- you can modify it
x = 43
print(x)


-- Lua has nil for Python's None

n = nil



-- what if you want to print
-- x is 43
-- this would fail:
--    print("x is " + x)
print('x is :' .. x)

-- string concatenation in lua is two dots: ..

print('my name ' .. 'is' .. 'Lua')

-- we will come to string formatting later

3. Control structures

{control.lua 3}
num = 42

-- lua does not use block syntax like Python
-- you have "then" instead of ":" and the block
-- ends with a "end" keyword

if num > 40 then
	print(num)
end

num = 0
-- lua has elseif vs python's elif
if num == 0 then
	print("num is zero")
elseif num < 0 then
	print("num is negative")
else
	print("number is positive")
end

-- what's equivalent of python's range?
-- Python:
-- for i in range(10):
--     print(i)
for i = 1,10 do
	print(i)
end

-- the same can be written in a single line
-- since we do not have to follow block syntax

for i = 10, 1, -1 do print(i) end

-- TODO: for comprehension

-- lua also has repeat loop construct
i = 12
repeat
	print(i)
	i = i - 1
until i < 10

-- There is no equivalent to Python's -= or +=

4. Functions

{functions.lua 4}
-- functions are defined with function keyword
-- vs python's def
function add(x, y)
	return x + y
end

print(add(3, 4))

5. Tables

Python has lists, tuples, dictionaries etc., In Lua, you make do with tables.

{tbls.lua 5}

-- this is a table that looks like a python list
lst = {1,2,3,9}

-- to iterate over it and print it, you use the 
-- `ipairs` function. become familiar with it, you will be using it a lot.
-- ipairs stands for inde pairs?
-- ipair gives to you the index, and the value

for i, v in ipairs(lst) do print(i, v) end

-- a table can also hold heterogenous types
stock = {"GOOG", "2020-05-29", 1426.82}
for i, v in ipairs(stock) do print (i, v) end


-- a table is really a dictionary
me = {} -- an empty table
me['name'] = 'Pradeep'
me['kids'] = 2

-- unlike python, there is no easy way to inspect the contents
-- of this dict^H table in the REPL
me
-- prints -- table: 0x7f84ae601100
-- unlike Python

[[
>>> me = {}
>>> me['name'] = 'Pradeep'
>>> me['kids'] = 2
>>> me
{'name': 'Pradeep', 'kids': 2}
]]

6. String manipulation

{string_manip.lua 6}
-- Python code
[[
def get_filename(url):
	"""given https://example.com/foo.html
	return foo.html
	"""
	if url.endswith('.html'):
	    return url.split('/')[-1]
]]

-- Lua does not have a big standard library like Python
-- so something as simple as the above code becomes
-- stackoverflow search :)

-- from https://stackoverflow.com/a/20100401
function split(s, delimiter)
    result = {};
    for match in (s..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match);
    end
    return result;
end

-- I'm going to progressively explore this till I get the
-- above Python program in Lua

-- The above split function returns a lua Table.

split("hello world", " ")
-- table: 0x7ffe977008f0

7. Using Modules

Python comes with a large standard library for everything from dealing with operating system, json, networking, email, http etc

Lua's standad library is not that extensive.

Importing a nonexistant module is a bit more helpful in Lua:

{modules.lua.txt 7}
require 'json'
stdin:1: module 'json' not found:
        no field package.preload['json']
        no file '/usr/local/share/lua/5.3/json.lua'
        no file '/usr/local/share/lua/5.3/json/init.lua'
        no file '/usr/local/lib/lua/5.3/json.lua'
        no file '/usr/local/lib/lua/5.3/json/init.lua'
        no file './json.lua'
        no file './json/init.lua'
        no file '/usr/local/lib/lua/5.3/json.so'
        no file '/usr/local/lib/lua/5.3/loadall.so'
        no file './json.so'
stack traceback:
        [C]: in function 'require'
        stdin:1: in main chunk
        [C]: in ?

Compared to Python:

{modules.py.txt 7}
>>> import foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'foo'

What Lua's error message tells us is:

Luarocks is Lua's package manager.

Once you have that installed [^1], you can install the lua-cjson library. From the name it appears it is a loadable C dynamic library.

{luarocks_install.txt 7}
$ luarocks install lua-cjson
                                                                                                                    1 ↵
Installing https://luarocks.org/lua-cjson-2.1.0.6-1.src.rock

env MACOSX_DEPLOYMENT_TARGET=10.8 gcc -O2 -fPIC -I/usr/local/opt/lua/include/lua5.3 -c lua_cjson.c -o lua_cjson.o
lua_cjson.c:743:19: warning: implicit declaration of function 'lua_objlen' is invalid in C99 [-Wimplicit-function-declaration]
            len = lua_objlen(l, -1);
                  ^
1 warning generated.
env MACOSX_DEPLOYMENT_TARGET=10.8 gcc -O2 -fPIC -I/usr/local/opt/lua/include/lua5.3 -c strbuf.c -o strbuf.o
env MACOSX_DEPLOYMENT_TARGET=10.8 gcc -O2 -fPIC -I/usr/local/opt/lua/include/lua5.3 -c fpconv.c -o fpconv.o
env MACOSX_DEPLOYMENT_TARGET=10.8 gcc -bundle -undefined dynamic_lookup -all_load -o cjson.so lua_cjson.o strbuf.o fpconv.o
lua-cjson 2.1.0.6-1 is now installed in /usr/local (license: MIT)

We can now use this module

{use_cjson.lua 7}
local cjson = require 'cjson'
saturn_moons = {'Titan', 'Mimas', 'Dione', 'Phoebe'}
text = cjson.encode(saturn_moons)
print(text)

8. Colophon

Status: Work In Progress

© 2020, Pradeep Gowda

Written in the literate programming style using Literate

Last-updated: Fri, May 29, 2020