Lua for Python Programmers

Types

Lua, like Python, is dynamically typed, so variables do not have types but values do. For dynamically typed scripting languages, type checking is done at run time. Python is strongly typed, meaning that all type errors are detected. For example, a number cannot be concatenated to a string using the + operator.

Is Lua strongly typed? Study and compare the following sets of expressions, noting that only the first lines are identical:

PythonLua
>>> 1 + 1
2
>>> "1" + "1"
'11'
>>> "1" + 1

TypeError: Can't convert 'int' object to str implicitly

>>> "1" + str(1)
'11'
>>> 1 + int("1")
2
> = 1 + 1
2
> = "1" + "1"
2
> = "1" + 1
2
> = 1 .. 1
11
> = "1" .. "1"
11
> = "1" .. 1
11

In Python the same ‘+’ operator is used for arithmetic and string concatenation, whereas in Lua there is a unique string concatenation operator. This means that in Lua there is no type ambiguity to be checked for when performing addition and concatenation between numbers and strings. The ‘+’ operator always performs addition, and the ‘..’ operator always performs concatenation. The implicit conversion from one type to another so that an operation can take place is called coercion. Lua uses coercion between numbers and strings in any context where one type and not the other is expected. The result is not ambiguous because the operand or parameter is always expected to be either a string or a number. Lua’s concatenation operator (although two characters long and not highly intuitive) makes the language more writable and readable than Python which uses the relatively more verbose type conversion functions.

So is Lua strongly typed or not? Lua only uses coercion between strings and numbers. Because string-numeric coercion is not an exception to strong type checking in Lua, it can still be classified as strongly typed, although along the spectrum from strong to weak typing, Lua is not nearly as strongly typed as Python. A more helpful name of type checking that better describes both Lua and Python is duck typing which describes dynamically typed scripting languages that perform type checking at run time.

The eight primitive types of Lua are nil, boolean, number, string, function, userdata, thread, and table. Python has many more primitive types, but implicit type conversions sometimes make the variations hidden or irrelevant. Consider that while Lua uses (without special build configuration) double-precision floating point for all numbers, Python has four numeric types: int, float, long, and complex. Does Lua need an integer type? Does Python? Notice how the following expressions are identical in Lua and Python:

PythonLua
>>> 9/5
1.8
>>> math.floor(9/5)
1
>>> -1 % 5
4
>>> .1 % 5
0.1
> = 9/5
1.8
> = math.floor(9/5)
1
> = -1 % 5
4
> = .1 % 5
0.1

So if the code is the same, then why does Python use a more complicated implementation? Actually, the given example is not a fair representation of Python integers. Python can represent arbitrary-length integers. Consider these results:

PythonLua
>>> fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915
608941463976156518286253697920827223758251185210916864000000000000000000000000
> = fact(100)
9.3326215443944e+157

One result is approximate whereas the other is exact. Also consider these results:

PythonLua
>>> fact(900)
67526802209645841583879061361800814224269427869589384312198268703685091643180416
96913244695269830379422601037057867290859319834769988692859190650103158765184697
67596811126095247870938480044286361868933952727844506303540802432176466580246966
59065951793757223520229235577548653833681102170973893746054649126415909143150172
86072115668581065575923001145013299217645498322753869634011261044702900233700488
78772663877045860772935854331516125188001477644611826808228670927866949828318386
41800997499819339206579415325649748486265233918911087114592440896594062675914294
92581671986217837467927209263752478693903629003592427178225373805988693392344787
77695830030167053633390314130691558375185247610783420526354756321131696187745492
75701480106933362990003732589370593557325299434734459295866728988740794174654391
47992600084884668670872973671320728520371273220127241083083691305263536508288872
51716360815871516034682911067546403982321466736273708959340907778288275495542324
36190464827998683927179246029919443251026464452337939599198528297828591122689960
62036123824831315807164339584840504726141268003987773376184987444732386791171263
00231717459682784657805585680670350138852750802921373604918751649477244642216935
33755035300065350065137490832039523382963747026185653050331832380991844842560750
92354377518858209648747695025441836519899967468441728626544278665159440478162294
69018791663829307141969082274601330276058178648773777121931421376254303537184482
69390732615776645283198828602917680224041088993892610506802195917247838900106910
69805703037919057105760584932311330863445200817988116561644976764835416122506696
79612976096987427379233893916152074411523193928456876733118992470853277034218629
72871644495409572259985563215471482083325653231777113271326579970310755604973969
70894947737425497448029465242702243670538018406400885345721451851527098556319541
29931452740576886344488124494458006176311627682431256064248447093720221499084635
72254912654907763445758543980999149122998104378965626781898655221443263601405152
07319970658508028873504020541737127725309624320000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000
> =fact(900)
1.#INF

This example reveals that Lua has a constant which is defined as being larger than any representable number. It also shows that Python can represent more numbers exactly. Python is therefore more reliable, and because no special syntax is needed to use arbitrary-length integers, there is no writeability trade-off. Lua’s advantage here is in processing speed. Under the hood, Python integers are objects that are allocated on a heap (Python doc.). Lua numbers are on the same low level as they would be in C.

Lua has one built in data structure called table. This multi-purpose structure is comparable to the Python dictionary in that both are mappings. They are different behind the scenes in that the indices of Python dictionaries are hashes of objects whereas the indices of Lua tables are references to objects. Practically speaking this means that in Lua if you have two distinct objects in memory (i.e. two tables) created with identical values stored in them, the two objects can index different values in a table. Note that identical strings are always the same object.

Lua
> a = {1, 2, 3}   -- new table created
> b = {1, 2, 3}   -- another list-like table
> t[a] = 1        –- another created with one value  
> t[b] = 2        –- ‘t’ now has two values
> = t[a]          –- retrieves a value from ‘t’
1
> = t[b]          –- proves that ‘a’ and ‘b’ are different
2
> c = "name" 
> d = "name"
> t[c] = "Andy"   –- the index is the string “name”
> = t[d]          –- the index “name” goes with “Andy”
Andy
> = t.name        –- syntactic sugar for string indices
Andy

Lua tables can play the part of lists, dictionaries, and even objects with instance variables and methods. The one table type has a lot of syntax options that make this possible. A small yet extremely important difference up front is that in Lua, the first item in a table or a string is located at index 1 and not 0. This difference is perhaps shocking to experienced programmers but probably expected for those who have never programmed before. Lua was designed with this in mind.

The single table type in Lua, with all its simple and flexible syntax, can make Lua more writeable than Python which has some different syntax for different data structures. The major trade-off is reliability. For example even though Lua tables can behave just like lists if you let table assign its own indices to new elements, there is nothing to stop that table with numerical indices from gaining a dictionary-like string index if you add it. An immutable tuple is also absent from Lua yet available in Python. Don’t worry; multiple assignments and return values are still supported by Lua. The following statements show that Lua does not use a tuple to perform multiple assignment whereas Python does use a data structure:

PythonLua
>>> x, y, z = 1, 2, 3
>>> x, y, z
(1, 2, 3)
>>> x, y, z = [1, 2, 3]
>>> x, y, z
(1, 2, 3)
> x, y, z = 1, 2, 3
> = x, y, z
1       2       3
> x, y, z = {1, 2, 3}
> = x, y, z
table: 00428B28 nil     nil