quirks can (and will) come around when migrate from one language to another, as languages often have different interpretations and implementations; it would be helpful if prominent inconsistencies are logged to avoid going over the getting started chapters again and again; this article is a list of pitfalls for people migrating to lua (from an unknown whatever language); in spite of word “pitfall” in title, this article does not mean to be a verdict about better or worse, but just poses the differences as they are;

here the lua reference in use is Programming in Lua (first edition) that aims at lua 5.0; these days most people would be on lua 5.4, but we are getting started, right? for those who want the latest language reference visit the page Lua 5.4 Reference Manual;

code examples below are in lua 5.4; so the code may not entirely conform to the lua reference in use;

each item has a series of ref tags that can be followed to find authentic information:

  1. (ref) a chunk can be a single line in interactive mode;

  2. (ref) global variables do not need to be declared or deleted; they can be assigned, and they are existent iff non-nil;

  3. (ref) lua is case-sensitive;

  4. (ref) lua has 8 basic types: nil, boolean, number, string, userdata, function, thread, and table;

  5. (ref) nil is different from anything but nil:

    > print(nil == nil)
    true
    > print(nil == false)
    false
    > print(nil == 0)
    false
    > print(nil == '')
    false
    > print(nil == {})
    false
    
  6. (ref) false and nil are false, everything else is true; zero 0 is true; empty string '' is true;

  7. (ref) lua numbers are all (double) floats; lua has no integer type, because lua floats have no rounding error upto 100,000,000,000,000;

  8. (ref) strings are immutable;

  9. (ref) single-quoted and double-quoted literal strings are the same;

  10. (ref) lua has automatic conversions between numbers and strings:

    > print(type("12" + 34), "12" + 34)
    number  46
    > print(type("12" .. 34), "12" .. 34)
    string  1234
    
  11. (ref) use tonumber and tostring for manual conversions between numbers and strings;

  12. (ref) lua table is associative array (aka: dict, map);

  13. (ref) all types except nil can be used as table key (aka: index); and key types can be mixed;

  14. (ref) table values evaluate to nil if not initialized;

  15. (ref) use table with integer keys to implement conventional array;

  16. (ref) use ipairs to iterate conventional array;

  17. (ref) lua supports functional programming; functions are first-class citizens;

  18. (ref) relational operators always result in true or false;

  19. (ref) lua inequality operator is ~= (not !=);

  20. (ref) values of different types are not equal:

    > print(0 == '0')
    false
    > print(0 == '')
    false
    > print(0 == {})
    false
    > print(0 == false)
    false
    > print(0 == nil)
    false
    
  21. (ref) lua compares tables, userdata, and functions by reference:

    > a = {}
    > b = {}
    > c = a
    > a == b
    false
    > a == c
    true
    
  22. (ref) logical operators are and, or, not;

  23. (ref) logical operators consider false and nil as false and anything else as true;

  24. (ref) both and and or use shortcut evaluation;

  25. (ref) lua lacks ternary operator: a and b or c is bug;

  26. (ref) operator not always returns true or false:

    > print(not nil)
    true
    > print(not false)
    true
    > print(not 0)
    false
    > print(not '')
    false
    > print(not {})
    false
    
  27. (ref) one can use (not not ...) to convert any value to boolean;

  28. (ref) operator not has higher precedence than relational operators (eg: ==):

    > print(not not 0 == 0)
    false
    > print((not not 0) == 0)
    false
    > print(not not (0 == 0))
    true
    
  29. (ref) table constructor can mix explicit and implicit indexes:

    > x = {'a', 'b', [8]='c', 'd', 'e'}
    > x[1]
    a
    > x[2]
    b
    > x[3]
    d
    > x[4]
    e
    > x[8]
    c
    
  30. (ref) mixing explicit and implicit indexes in table constructor can cause trouble:

    > x = {'a', 'b', [2]='c', 'd', 'e'}
    > x[1]
    a
    > x[2]
    b
    > x[3]
    d
    > x[4]
    e
    > x[2]
    b
    
  31. (ref) in multiple assignment, values are evaluated then assigned; so swap works:

    > x, y = 3, 8
    > x, y = y, x
    > print(x, y)
    8       3
    > a = {4, 9}
    > a[1], a[2] = a[2], a[1]
    > print(a[1], a[2])
    9       4
    
  32. (ref) in multiple assignment, values are adjusted to variables; extra variables are assigned nil; extra values are discarded;

  33. (ref) local variables are created with keyword local;

  34. (ref) local variables have block scope; the scope begins after the declaration and goes to end of block;

    local x = 1
    for i = 1, 2 do
        print(x)
        local x = i * 2
        print(x)
    end
    
    1
    2
    1
    4
    
  35. (ref) local variable declaration with assignment has same rule: extra variables are assigned nil; extra values are discarded;

  36. (ref) local variable declaration without assignment initializes all variables with nil;

  37. (ref) one can create an explicit block with do-end:

  38. (ref) condition expression in if, while, etc. may be any value: false and nil are treated as false, others true;

  39. (ref) numeric for: all three expressions are evaluated only once;

  40. (ref) numeric for: loop variables are local variables visible only inside the loop;

  41. (ref) numeric for: never change the value of loop variables;

  42. (ref) generic for: loop variables are local variables visible only inside the loop;

  43. (ref) generic for: never change the value of loop variables;

  44. (ref) generic for: use ipairs to traverse conventional arrays; use pairs to traverse associative arrays;

  45. (ref) there is an implicit return at the end of any function that returns nothing;

  46. (ref) one can use colon : for object-oriented calls (aka: methods): o:foo(x) is the same as o.foo(o, x);

  47. (ref) function parameters work like local variables, initialized with function arguments;

  48. (ref) extra function parameters are assigned nil; extra function arguments are discarded;

  49. (ref) lua lacks default arguments;

  50. (ref) functions can return multiple results; the number of results depend on the circumstances of the call;

  51. (ref) a function call used as an expression gives only the first result;

  52. (ref) a function call gives all results only when it is the last expression in a list of expressions;

  53. (ref) function results are appended with nil as needed;

  54. (ref) table constructor collects all results from a call, without any adjustments;

  55. (ref) one can force a call to return exactly one result using an extra pair of parentheses:

  56. (ref) return f() returns all values returned by f;

  57. (ref) return (f()) returns one single value;

  58. (ref) unpack takes an array and returns all its elements as results (starting from 1);

  59. (ref) three dots ... parameter collects a variable number of arguments into hidden table arg;

  60. (ref) a function can have some fixed parameters plus a variable number of parameters;

  61. (ref) lua lacks named arguments; simulate that by passing a table argument;

  62. (ref) functions are anonymous:

    function f (x) return x+1 end   --same
    f = function (x) return x+1 end --same
    
  63. (ref) local function syntactic sugar handles recursive functions, but not indirect recursive functions;