Chapter     2

The Pillar of Creation: Lua

In Chapter 1, you looked at Corona at a high level and began to get a feel for what it offers. Corona provides the APIs you need to develop great cross-platform mobile apps, but an API is only half of the equation. If all you had was the API, you’d be a carpenter with a stack of wood but no hammer and nails to build anything with.

Fortunately, the other half of the equation—the hammer if you will—is provided to you in the form of Lua.

In this chapter, you’ll dive into Lua and start to get familiar and comfortable with it. As I mentioned in Chapter 1, I assume that you have had at least some programming experience; however, I will assume a very low baseline of knowledge. If you’re a very experienced programmer, you can zip through this chapter in no time, or quite possibly even skip it entirely and just pick up the syntax as you go. If that’s not you, then you should get a good foundation to build on in this chapter, and you’ll build upon that knowledge in the chapters to come.

A Jack of All Trades

Lua is a free (released under the developer-friendly MIT open-source license) extension language, which means that it isn’t a language you write stand-alone executable programs in à la Java or C/C++. Instead, Lua is always embedded within a host program. The host program is then able to execute a piece of Lua code and manipulate the variables within that piece of code. The host can also register functions written in Standard C that can then be called from the Lua code, which is where a lot of the power and performance associated with Lua comes from (and is a big part of the reason Lua works so well for Corona).

Lua is designed to be used as a scripting language to support the needs of any program that embeds it. The ability of Lua to be embedded, along with the fact that it allows usage of C functions, permits Lua to be used to create meta-languages or so-called domain-specific language (DSL), meaning a scripting language suitable for the specific task its host program provides.

Lua offers support for such common concepts as object-oriented programming, functional programming, and data-driven programming. It is a relatively simple language on its own that, through its extension and embedding capabilities, provides nearly unlimited power to its host. Based on this description it should be obvious that Corona acts in effect as the host program for the Lua scripting language. If you’ve ever used Visual Basic for Applications (VBA) in an Excel file, or written a module for World of Warcraft, you will understand the basic concept (and in the case of World of Warcraft, you’ll know about Lua already since that’s what it uses). You use the relatively simple Lua language written against the Corona API to create your application.

Lua is an automatic garbage-collection language that uses an incremental mark-and-sweep collector (although the collector type could be different based on Lua implementation). Which collector it uses isn’t terribly important in any case, but the fact that it is a garbage-collecting language is. This means that you as the programmer never have to worry about allocating and deallocating memory; Lua handles it for you. Lua periodically will collect dead objects; that is, objects that are no longer referenced from your code. As you’ll see later, this means that while you don’t have to worry about deallocating memory, you do need to worry about getting rid of references to objects you no longer need. Otherwise you can get in the way of the garbage collector and cause yourself problems, especially on resource-constrained mobile devices.

So, with the 10,000-foot overview of Lua in mind, you can take a look at some of the basics of the language.

The Bare Necessities: Lexicology

Lexicology refers to the study of words: their nature, meaning, and relationship with other words. In programming that means talking about things like keywords and syntax.

Yes, you caught me—I just wanted to use a fancy word to describe a basic concept!

Lua, lexicographically speaking (there, I did it again!) is free-form, meaning it doesn’t care about spaces, not unlike many other modern programming languages. How you choose to format your code is entirely up to you. Of course, good conventions are good in any language and Lua is no different. Whatever style you choose, simply be consistent with it and you’ll be fine. As you progress through this book, you’ll see a consistent coding style in my code because it’s something I’m hyperaware of and vigilant about. You may or may not agree with every style choice I make, and that is perfectly fine, but you will see a consistency to it.

In any case, Lua will accommodate you just fine!

Lua is one of the C-inspired (more or less) languages syntactically, which means, among other things, that it is case sensitive. So, myspaceshipsprite is different from MySpaceshipSprite.

Statements in Lua can end with a semicolon; however, that is optional.

AN INTERESTING NOTE ON SEMICOLONS

If you have an interest in language construction as I do, then you might be interested to know that Lua, at least the interpreter used in Corona, doesn’t seem to suffer from so-called semicolon insertion, as some other languages such as JavaScript do.

Not sure what I mean? Consider this:

return { p : "yes" };

In JavaScript, that returns an object with a single property p with a string value of "yes". Simple enough, right? Now, consider this:

return
{ p : "yes" };

What happens there? Well, you may think it works the same because semicolons are optional in JavaScript; however, you’d be wrong, at least in some JavaScript engines. JavaScript tends to insert semicolons for you at runtime, and one is inserted at the end of the return statement. The section in braces then becomes an anonymous block that does nothing. So, any code calling a function with this code in it will always get null back. It’s a subtle little gotcha that’ll drive you nuts if it bites you!

But, as I said, Lua doesn’t seem to do this, in Corona anyway (perhaps other interpreters do it, I’m frankly unsure). In any case, this is just an interesting (to me at least) aside; the bottom line is that semicolons are truly optional in Lua under Corona and I’m not aware of any gotchas such as this that can crawl out of the woodwork regardless of whether you use them or not. I use them constantly out of habit since I do a great deal of JavaScript work, but it is entirely your choice.

Names in Lua, be they for variables, functions, or anything else that can be named, can be any combination of letters, digits, and underscores of any length. They cannot however, begin with a digit.

Note that underscore is the only punctuation-type character that can be used in a name. For example, while this:

My$Variable1

would be perfectly valid in most other languages, in Lua it isn’t. Changing the dollar sign to an underscore, however, would make it valid.

In addition, by convention, elements beginning with an underscore indicate elements supplied by Lua itself. Therefore, while you can start a name with an underscore, you probably shouldn’t, just to avoid the possibility of conflicts.

Blocks in Lua are collections of statements executed as a unit and that share an execution context. The most common block you’ll encounter is probably the function:

function MyFunction()
end

Everything between those lines is a block. Other blocks you’ll see frequently are if statements and loops.

if a == true then
end
for i = 1, 10, 2 do
end

You can also define an anonymous block this way.

do
-- Some code
tnd

The concept of blocks is important to the discussion of scope, which I discuss later in the “A Place for Your Stuff: Variables, Values, and Types” section. You should also be aware that the term chunk is often seen in discussions of Lua. It’s really just a synonym for block, and both simply refer to a collection of statements that share some scope.

The Keys to Success: Keywords

A programming language wouldn’t be a programming language without keywords: the list of words that have special, specific meanings to the interpreter and that you can’t use yourself (outside of string values, of course). Lua’s keywords are as follows:

  • and
  • break
  • do
  • else
  • elseif
  • end
  • false
  • for
  • function
  • goto
  • if
  • in
  • local
  • nil
  • not
  • or
  • repeat
  • return
  • then
  • true
  • until
  • while

That’s actually a remarkably short list of keywords—just 22 in total! Compared to other languages, JavaScript is close with about 29, but then something like Java has around 50! It’s amazing what you can actually accomplish with a language with so few keywords, which should probably be a clue that most of Lua’s power, and by extension Corona’s, comes from an extensive function library, as you’ll see throughout this book.

In the same vein as keywords are tokens: those special characters or combinations of characters that have specific meaning when building up expressions. Lua recognizes the following tokens:

  • +
  • -
  • *
  • /
  • %
  • ^
  • #
  • ==
  • ∼=
  • <=
  • >=
  • <
  • >
  • =
  • (
  • )
  • {
  • }
  • [
  • ]
  • ;
  • :
  • ,
  • .
  • ..
  • . . .

Most of those will be familiar as they have the same meaning as in any other language that uses them. However, a few are less common or unique to Lua, so I’d like to call those out now:

  • ∼= Not equals (!= in many other languages)
  • .. String concatenation (+ or & in many other languages). Note that + in Lua is purely mathematical and is not overload from concatenation as it is in many other languages.
  • . . . Varargs, meaning a function that can accept a variable number of arguments

Making a Statement: Commenting

Commenting your code is always a good thing and Lua gives you two forms of comments to choose from. The first is termed a short comment and looks like this:

-- I am a short comment
a = 5;
b = 6; -- Or at the end of a statement is fine too

A short comment can be on its own line or at the end of a line, whichever you prefer, but always starts with a double hyphen (except when it appears inside a string). A short comment runs until the end of the line in either case.

If you guessed there’s such a thing as a long comment as well, then pat yourself on the back because there is! It looks like this:

--[[ I
Am
a long
comment
]]--

--[[ and ]]-- denote the start and end of a long comment, respectively, which can span multiple lines (but of course doesn’t have to). Long comments are frequently used to temporarily deactivate a section of code, but can also be seen for function and program headers or other comments that require more text.

Which you choose is entirely up to you. Personally, I nearly always use short comments, even if I’m writing a long comment that spans multiple lines; I’ll just have many lines of short comments in that case. I prefer this because to my eyes it stands out more in the code. Likewise, even if I’m commenting out a section of code during development, I’ll usually do it with short comments (albeit automatically using an IDE commenting tool). Especially when you have an editor that does syntax highlighting, which oftentimes means comments show up in a duller color, it helps to quickly identify what code is live and what code is disabled. This is all entirely personal choice, though.

A Place for Your Stuff: Variables, Values, and Types

Lua is a dynamically typed language, which means that variables can point to values of any type, and can point to values of different types at various times. For example:

myVar = 123;

The type of value myVar points to is a number here, but note that there is no type declared for myVar (assume this is the first statement in the program and I’m not being sly and hiding something!). If the next line of code is:

myVar = "123";

That’s perfectly valid; now, myVar points to a string.

All types of values are first-class citizens in Lua, meaning they can be stored in variables. This means that in Lua, a variable can hold a reference to things like numbers and strings, just like any other languages, but they can also hold a reference to things like functions and something called tables, which I’ll get to shortly. This means that you can pass around references to all these things, and return them from functions, as you would any “primitive” data types.

Speaking of data types, Lua supports six types of data:

  • nil: This is usually called null in other languages and indicates a variable that hasn’t yet been assigned a value
  • Boolean: A value of true or false. Note that true and false versus 0 and 1 in Lua is a bit tricky. Consider the following example:
    a = 1;
    if a == true then
      print("y");
    else
      print("n");
    end

    What is printed here? In many languages, it would be “y”, but in Lua it’s “n” because 1 doesn’t equal true.

    However, consider this code:

    a = 1;
    if a then
      print("y");
    else
      print("n");
    end

    You’d expect to see “n” there too, wouldn’t you? Contrary to logic, “y” gets printed!

    The point I'm trying to make here is simply this: if you’re talking about a true or false value, use true and false! Don’t try and use 0 and 1 as aliases for them. That will ensure you don’t run into any unexpected problems.

  • number: All numbers in Lua are real, double-precision floating-point numbers, plain and simple.
  • string: The usual array of characters. Note that unlike C you do not have to worry about termination characters.
  • function: A named, callable block of code, pretty much like any other language out there. Note that in Lua a function is considered an object like a table is. Speaking of which . . .
  • table: The table is pretty much the fundamental data structure in Lua and can be thought of like objects in most other object-oriented languages (although, naturally, they have their own specific characteristics). Tables get their own section coming up shortly so we’ll hold off on any more specifics until then.

Variables can be declared anywhere at any time by simply assigning a value to them (and, as mentioned before, the type of that value can change at any time). If you simply write this:

a = 5;

then you’ve created a global variable that will live as long as your program executes. The scope of this variable is anywhere within the program; it can be accessed (and changed) from anywhere. The only way to get rid of that variable would be to do:

a = nil;

Then, the garbage collector will deallocate its memory during its next sweep.

The other scope that Lua understand is block scope, which means that a variable is declared, used, and deallocated within the context of a block of code. The most common place to see this is in a function:

function MyFunction()
  local myVar = 6;
end

Here, the variable myVar existing only within MyFunction(), as denoted by the local keyword, and when the function exits myVar will be garbage-collected, even if you don’t assign nil to it.

Note  Because of scope resolution—that is, the procedure Lua uses to locate a variable—declaring things using local whenever you can is a good practice in terms of performance.

For example, if you have in a function and in it you write:

a = a + 1;

Lua will first look to see if there’s a local variable named a. If it can’t find it, which of course it won’t here since there’s no local keyword before it, it’ll start going up the scope chain. It’ll ask: is the variable maybe in a containing block? This will continue until, finally, Lua looks in global scope, where it finds the variable you’re incrementing (assuming it was declared in global scope of course, which for the sake of this conversation, it was). All of that work impacts performance (albeit only a small amount if this isn’t happening thousands of times a second).

So, unless you actually need (or for some reason want) a variable to be global, declare it locally in some limited scope, and preferably as close to its usage as possible, and shorten the scope chain lookup that has to occur, to keep your app humming along.

Note that this can lead to some interesting situations:

myVar = 6;
function MyFunction()
  print(myVar);
  local myVar = 5;
  print(myVar);
end
MyFunction();
print(myVar);

What gets printed here? Three values: 6, then 5, then 6. The first print() inside MyFunction() prints the value of the global myVar. Then, we create a new myVar scoped to MyFunction(), which essentially masks the global myVar. Then, once we print myVar after returning from MyFunction() we’re again printing the value of the global myVar, which wasn’t changed by anything done in MyFunction().

What if you want to access the global myVar inside MyFunction() after creating the local version of myVar? Well, there is a way:

myVar = 6;
function MyFunction()
  print(myVar);
  local myVar = 5;
  print(myVar);
  print(_G.myVar);
end
MyFunction();
print(myVar);

Try this code and you’ll see four values printed: 6, then 5, then 6, then 6. This works because in Lua, all global variables are automatically added to an object _G, which represents global scope. So, you can always disambiguate when you have masked variables, no problem.

That being said, you’ll save yourself a lot of hassle by avoiding masking like that anyway!

Also, note that while I showed how this works in the context of a function, the rules are the same inside any block. So, if you have an if statement, or a loop, and declare some local variables within it, they will be automatically cleaned up after the block completes.

One exception to that rule is if you do something like this:

myVar = nil;
function MyFunction()
  local lVar = 1;
  myVar = lVar;
end
MyFunction();

Here, after the call to MyFunction(), the variable lVarwill not be garbage-collected because myVar references it, and it is global-scoped so it won’t itself be garbage-collected. The bottom line is you have to be careful not to retain references to anything you don’t really need to, or you’ll quickly run into memory leaks, meaning memory that can’t be garbage-collected. For a variable that holds a number of a small string, that’s probably not going to cause you any problems in the long run (unless you’re creating such variables a lot in a tight loop). However, when I later get to creating graphics and audio, which can take up a lot of memory, it can quickly lead to out-of-memory errors, especially on resource-constrained mobile devices, so better to always be thinking about it and avoid these scenarios entirely.

Lua also offers a multiple assignment form, allowing you to do things like:

local x, y = 3, 5;

This leads to a neat trick to swap the values of two variables:

x, y = y, x;

That’s all it takes!

Lastly, simple arrays are defined using braces like so:

local myArray = { "1", "2", "3" };

You can then access the elements using brackets such as myArray[2]. Note that arrays are one-based, so the element "1" is retrieved with myArray[1].

Expressing Yourself: Expressions and Operators

Expressions in Lua aren’t much different than in other languages. The usual mathematical tokens operators such as addition (+), subtraction (- or negation if used unwarily), multiplication (*), and division (/) are supported, along with modulo (%) for getting remainders of divisions and exponentiation (^).

Lua also supports the usual suspects of relational comparison operators: equals (==), not equals (∼=), less than (<), greater than (>), less than or equal to (<=) and greater than or equal to (>=). Each of those returns true or false as you’d expect. Note that == will first compare the types of the variables and will return false if they aren’t the same. In other words:

local varA = 5;
local varB = "5";
print(varA == varB);

That will print false since varA is a number and varB is a string, even though their values appear to be the same.

Note  It doesn’t matter to anything at all, but it always makes me chuckle when I see < and > because as a child I had a friend who would explain those symbols by holding up his hand in the same position and say, “Alligator go that way.” Pointless, I know, but it brings a smile to my face thinking about it!

In fact, the previous code would return true if it weren’t for that rule because, being a dynamically typed language, Lua has another benefit: conversions between numerics and strings usually will happen without your intervention and without any difficulties (it also helps that there’s only one numeric type to deal with). Therefore, you can do things like:

local a = 5;
local b = "6";
print(a + b);

That will print “11”, not “56” as in some other languages (or, worse still, produce an error of some kind).

Objects, such as those fabled tables that I’ve yet to delve into, are compared based on references. So this:

local tableA = { prop = "123" };
local tableB = { prop = "123" };
print(tableA == tableB);

will print false because we’ve created two different objects there, and even though they contain the same internal structure and data they are still two completely independent objects.

Lua supports the usual logical operators and, or, and not. For these, nil is considered false and any other value is considered true. Because of this, the negation operator (not) will always return true or false regardless of the type being negated. In all cases, conjunction (and) and disjunction (or) operators will use short-circuit evaluation. This means that the second operand will not be evaluated unless it needs to be (if the first operand is false in the case of conjunction, or true in the case of disjunction, then the second operand does not need to be evaluated since the first is enough to determine the outcome of the evaluation).

As briefly mentioned earlier, the concatenation operator (..) is used to combine values. Note that I was careful not to say “string” there because it works just as well for numerics, and you can mix and match just fine. So, for example:

local varA = 5;
local varB = 6;
local varC = "6";
print(varA .. varB);
print(varA .. varC);

In the first print() statement we’re concatenating two numbers, but the result is to print out “56” because they aren’t being added. The second print() statement likewise displays “56” because here we have a number and a string, so the number is converted to a string and the two strings concatenated. While in other languages the + operator serves both purposes, that isn’t the case in Lua; so clearly you have to be aware that + and .. are distinctly different in Lua and use them appropriately.

The last operator worth mentioning is the length operator (#). This can give you the length of things like strings or arrays. So, if you have:

local myString = "Testing";
local myArray = { 1, 2, 3 };
print(#myString);
print(#myArray);

that will print “7” and “3” because there are seven characters in myString and three elements in myArray.

Note  In Lua there is no ternary operator, so you can’t do things like a:b?c like you can in many other languages. The equivalent in Lua would be "a and b or c". Note however that if b is false then this idiom breaks down and doesn’t work as expected. To save yourself headache it’s better to simply forget all that you know about ternary expressions and just write them out long-form with an if statement.

This also works for tables—speaking of which . . .

Let’s Table This Discussion: The Mythical “Table”

So, these table things that I’ve mentioned a few times, what are they all about? Well, in simplest terms, they are objects. More precisely, they are associative arrays; that is, arrays of elements that can be accessed using an identifier (and as you’ll see, the identifier, called a key, can be a numeric index value or something nonnumeric). Tables aren’t all that dissimilar from dictionaries in other languages, but they actually share qualities of arrays and dictionaries in Lua, making them that much more flexible and powerful.

You saw an array just a little while back:

local myArray = { 1, 2, 3 };

You can access the elements in the array using bracket notation:

myArray[2]

This retrieves the second value in the array, 2 in this case. This is sometimes referred to as a plain old array. However, it is in fact a table! To make that more explicit, change the code a bit:

local myTable = { one = 1, two = 2, three = 3 };

You’ll notice that you used braces in either case, but now, what you’ve done is given each element in the table a specific key name that you can use to look up the value. So, to get the value of the element identified by the key "two", use:

myTable["two"]

This returns 2 again, as before.

You actually have a choice here: because the key name "two" becomes what we term a property of the table, you can instead use dot notation:

myTable.two

This returns 2 as well. Which you use is mostly a style choice, although the general convention most people follow is to use dot notation. However, the bracket notation can come into play when you want to access the properties dynamically:

local myTable = { one = 1, two = 2, three = 3 };
local whichProperty = "two";
print(myTable[whichProperty]);

Which property is printed is dependent on the value of whichProperty, so in this case we have to use bracket notation because trying to do:

print(myTable.whichProperty);

would actually mean: “print the value of the property named whichProperty” when what we really mean is “print the value of the property named by whichProperty”.

You may be asking yourself, “Self, doesn’t that mean that an array is really just a table?” The answer is yes, it is! When you define an array, what you’re really doing is defining a table that just happens to use numbers as key values. To illustrate this further, look at this code:

local myTable = { };
myTable[1] = "Corona";
myTable[2] = "SDK";
myTable[3] = "Rocks!";
print(myTable[2]);

This prints “SDK” since that’s the element defined by the number 2 (or at index 2 you could say, even though it should be apparent now that saying that isn’t technically accurate).

The other thing to notice is that you can indeed create an empty table, as is done in the previous code, and then add properties to it later, and it doesn’t matter if they are numerically keyed or not. That means you can do things like:

local myTable = { };
myTable.prop1 = "abc";
myTable["prop2"] = "def";
myTable[3] = "ghi";

You can now access prop1 by using:

myTable.prop1

or

myTable["prop1"]

The same holds true for prop2. However:

myTable[3]

is the only way you can get the value “ghi”. Trying to do:

myTable.3

is not valid and results in an error.

Do you remember that length operator (#) from earlier? An important point to remember is that it will only work for tables that have purely numeric keys. For the previous code, if you do:

print(#myTable)

you’ll get 0 because we’ve mixed and matched numeric and nonnumeric keys, so the length operator can’t determine the length. More to the point, talking about the length of a table with both numeric and nonnumeric key values doesn’t really make sense. You could argue that length in this context means “how many elements does the table contain,” and that’s not unreasonable, but it’s just not the way it works.

That’s why, in most cases, using either numeric keys or nonnumeric keys exclusively in a given table is the way to go, if for no other reason than not confusing yourself when your length operator doesn’t work as you expect!

Last, there are no limitations to the types of data a table’s properties can reference. Numbers and strings naturally are fine, as we’ve seen, as are other tables:

local myTable = {
  innerTable = {
    propA = "abc";
  }
}

Here, myTable.innerTable.propA retrieves the value “abc”. Plain old arrays work fine as well (i.e., innerTable could be a plain old array).

Table properties can also reference functions. I haven’t really discussed functions specifically yet, although you’ve seen them a bit (I’ll get into functions next), but for the sake of completing the discussion on tables, here’s an example of how that looks:

function myFunction()
end
local myTable = {
  funcA = function()
  end,
  funcB = myFunction
};

This shows two different approaches: you can define a function inline in the table (funcA) or you can set a property of the table to reference an existing function (funcB referencing myFunction()).

Note  One quick point: if you define an array using a={x,y,z}, then the first element is index 1. However, there’s nothing to stop you from later on doing a[0]=w and then accessing the elements of the array starting with index 0. However, all Corona API functions, and most Lua functions, assume an array begins with index 1, so unless you have a really good reason for doing otherwise, always treat arrays as beginning with index 1.

Getting Functional: All about Functions

Functions in Lua are quick and easy to define:

function add (num1, num2)
  return num1 + num2;
end

This will define a function called add() in global scope (assuming this isn’t itself inside a function) that accepts two arguments named num1 and num2, adds them together, and returns the result. Note that the arguments have no type specified, so they can be of any type at runtime, allowing quite a bit of flexibility in designing your functions. Of course, since this function adds two numbers you’d expect to only ever pass numbers to it, and in fact you’d have to add some logic to ensure that’s the case if there was a possibility of other types being passed in. For example, maybe you want to allow passing either two numbers or two tables, each with a number property, and your function adds the properties together and then returns a new object with the result as a property. You could do this if you wrote the function to tell the difference between a plain number and a table as arguments and act accordingly.

Also, note that there is no return specified in the function definition. You simply either return a value or not (the caller will get nil back if you don’t explicitly return a value).

A function can also be a property of a table:

local myTable = {
  add = function(num1, num2)
    return num1 + num2;
  end
};

Now you can do:

function add(num1, num2)
  return num1, num2, num1 + num2;
end

A function can also return multiple results:

local n1, n2, result = add(1, 2);
print(n1, n2, result);

This will print “1  2  3”, echoing back the input as well as returning the result. This capability isn’t used all that often, but it certainly is used and you are free to do so when appropriate.

Not only can a function return multiple values, but also it can accept a variable number of arguments:

function va(. . .)
  print(arg[1], arg[2], arg[3]);
end
va(1, 2);

Here, the output would be “1  2  nil” because we’re passing in two arguments. The function is defined to take a variable number of arguments, as denoted by the three dots in the argument list. Inside the function, we access the arguments by means of the intrinsic arg variable. Since we only passed two arguments the third one, arg[3], is nil. You can also do:

function va(arg1, arg2, . . .)
  print(arg1, arg2, arg[1], arg[2], arg[3]);
end

This way, if you know you always have two arguments, followed by some number of additional arguments, you can write the function accordingly. The only rule is that the three dots must be at the end of the argument list.

Note  As a general rule, I suggest nearly always write your functions as properties of a table. This effectively uses the table as a namespacing mechanism, which tends to help avoid naming conflicts (your table names might still conflict of course, but it’s usually functions that wind up with conflicts). In fact, much (maybe even most) of the Corona API is written this way.

As with properties, you can add functions to an existing table:

local myTable = { };
myTable.add = function(num1, num2)
  return num1 + num2;
end;

Now, if you’ve done some object-oriented programming you may have noticed that I haven’t used the term method to this point to describe functions that are properties of a table. That’s because technically what I’ve defined so far aren’t methods. The difference has to do with a special reference that is available in a function known as self.

When you call myTable.add() with the code shown earlier, the add() function has no notion of context. That is, it doesn’t really know that it’s a property of myTable. This is contrary to the object-oriented concept of encapsulation and may seem a bit odd at first. To make it seem more like what we would typically expect and give it context, you have to use a slightly different notation to define and call it:

local myTable = { };
function myTable:add(num1, num2)
  return num1 + num2;
end;

The difference, of course, is the use of colon instead of dot when adding the function to the table. Now, when you want to call add() you use:

myTable:add(1, 2);

The difference, aside from the simple syntax swap of colon for dot, is what happens inside the add() function (which is now properly termed a method). You now have access to the intrinsic variable self, which is a reference to myTable. This allows you to do this:

local myTable = {
  n1 = 5,
  n2 = 6
};
function myTable:add(num1, num2)
  return self.n1 + self.n2 + num1 + num2;
end;
print(myTable:add(1, 2));

Now, the result printed is “14” because add() adds not only the two arguments passed in to it but also the values of the properties n1 and n2 of myTable, which we can access via the self reference variable.

Using the colon when calling a method is in fact just a shortcut. You could still call add() using dot notation like so:

myTable.add(1, 2);

However, if you were to execute that you’d get an error. You would find that the value of self is 1, the value of num1 is 2 and the value of num2 is nil. The reason is that when you use colon to call a method, the self reference is automatically passed, but when using the dot notation it isn’t. To fix this you would have to manually pass a value for the self reference:

myTable.add(myTable, 1, 2);

Now you would have a valid reference to myTable as self, and num1 and num2 would have the values 1 and 2 respectively as expected. However, from a syntactic perspective, using colon makes the code a lot clearer and less verbose and is therefore the preferred idiom.

Note  This also raises the possibility that you could pass a reference to another object than the one a method belongs to, allowing for a kind of polymorphism (not to mention some convoluted code that’ll likely give you problems down the road). I don’t recall ever seeing this done, frankly, but as a technical matter, it is in fact possible.

In Lua, and therefore Corona, there are a handful of global functions available for your use. Many of them are infrequently used, but a few are used more often. I’ll go over a few of them now.

First is ipairs(). This accepts as an argument a table and returns an iterator function, the table, and zero. This allows you to iterate over the pairs or keys and values in the table like so:

for i, j in ipairs(t) do
  -- Do something
end

The trick here is that the array must be numerically indexed and not have so-called holes—that is, nil elements—because ipairs() will stop with the first one it hits. But if you know your array has holes, or if you want to iterate over all the elements of a table regardless of key type, you can use the related pairs() function, which works essentially the same but will continue until there are no more elements in the table.

Note that with either you should not modify the structure of the table during iteration; that is, add or remove elements. You can of course change elements, although even there you should take care not to nil something that you haven’t iterated over yet and thereby stop your iteration sooner than planned in the case of ipairs().

Next is print(). You’ve seen this a bunch of times so far and it is used simply to echo some string to the console window. You can pass it multiple arguments and they will be separated by a tab, and you can usually pass any sort of type to it and get some sort of result, even if it isn’t particularly helpful (a table, for example, will be shown as a reference number).

After that there is tostring(). This converts its argument, which can be any type, to a reasonable string representation. This might not be quite as helpful as it otherwise is if it weren’t for the fact that when passed a table that has a __tostring field in its metatable, it will call the function it points to.

Err, wait, what’s a metatable you ask? Well, every table has a metatable attached to it, which literally is just another table embedded in it. This table has specific keys in it that, when populated, can change the behavior of the table in some way. Say you have a table:

local t = {
  prop1 = "frank"
};

Now say you want to be able to control what tostring() spits out when passed this table. The first step is defining a function that will do the work for you:

function t:ts()
  return "t = [ prop1 : " .. self.prop1 .. " ]";
end;

The format of the string you return is of course entirely up to you. Now, we could certainly just call t:ts() any time we want. However, to use tostring() we need to attach a metatable to this table and provide a value for the __tostring key:

setmetatable(t, {
  __tostring = t.ts
});

The setmetatable() is another of the useful global functions that does precisely what its name implies. Now, we can do:

print(tostring(t));

This will return a string “t = [ prop1 : Frank ]” and print it to the console. But, as it happens, calling tostring() like that is actually redundant! As long as you set the metatable __tostring key value appropriately, you can simply do:

print(t);

Your ts() function will be called and you’ll get the output expected.

There are a number of other metatable keys that are of some use, however, in my experience they are not used all that often. As such, I’ll leave them for your own exploration, although, who knows, you may encounter one or two of them later in this book in the course of building a game!

Another useful global function is type(), which returns the type of the argument passed to it. This can be number, string, Boolean, table, function, thread or userdata. This is how you can implement polymorphism as I alluded to earlier, since you can determine the type of an argument to a function and act accordingly.

The final function I want to mention is pcall(). Normally, when an error occurs in Lua, it gets propagated upwards, normally to the host program to handle. Lua doesn’t have the notion of try/catch, but it does have pcall(), which serves a somewhat similar purpose. You pass it a reference to a function (along with a list of arguments) and it executes the function in protected mode and catches the error if one occurs. If the called function executes without error than you get back true plus the returned value. If an error occurs then you get back false plus the error. By way of example:

function goodFunction()
  return "goodFunction() done";
end
function badFunction()
  tolowercase(a);
end
print(pcall(goodFunction));
print("Now calling badFunction()");
print(pcall(badFunction));
print("Continuing");
badFunction();
print("Shouldn't see me");

The output here is:

true    goodfunction() done
Now calling badFunction()
false    attempt to call global 'tolowercase' (a nil value)
Continuing

The final print("Shouldn't see me"); doesn’t execute because a runtime error occurs as a result of calling badFunction() the second time without wrapping it in pcall(). The first time we call it with pcall() the error is effectively handled and the interpreter doesn’t stop. The second time, though, the error stops the program cold.

PCALL(), XPCALL(), AND PERFORMANCE

There’s also an xpcall() function that is slightly different than pcall(). With xpcall() you pass as the second argument an error-handler function that will be called. This allows you to have a standardized error-handling mechanism that is attached to all your protected function calls.

Note that both pcall() and xpcall() do indeed introduce a performance overhead to your function calls. Therefore, you really only want to use them during development, or in production code for functions that aren’t called in time-sensitive areas of your code, or where you’re calling a function that you know could result in an error. (Of course, if you already know that, how about just avoiding the error in the first place?!)

Taking Control: Control Structures

You can control the flow of control in your Lua code using a handful of statements, beginning with the well-known if statement, which takes the general form:

if XXX then
  -- Do something when XXX is true
elseif YYY then
  -- Do something with YYY is true
else
  -- Do something in all other cases
end

XXX and YYY are any expression that evaluates to a Boolean. Both false and nil are considered false, while all other values are considered true. Note that the number zero and an empty string are therefore considered true, which can be tricky if you forget!

Of course, all elseif clauses and the else clause are optional. Note that Lua does not have the common switch/case construct. The way to simulate that in Lua is simply a block of if/elseif statements.

Knocked for a Loop: The for, while, and repeat Constructs

Lua supports a handful of iterative statements including the well-known for loop, while loop, and repeat.

A for loop takes this general form:

for variable = start value, end value, step do
  -- Some code to repeat
End

So, a valid for loop that prints the numbers from 2 to 17 by 3 using the variable i as a counter would be:

for i = 2, 17, 3 do
  print(i);
end

The while loop takes the form:

while expression do
  -- Some code to repeat
End

As usual, the code inside the while loop will only execute if expression is true, so if it starts out false the code inside the loop will not execute even once.

Note that unlike other languages, there is no do/while construct to ensure your block executes at least once. There is, however, a way to simulate it, and that’s by using the repeat construct:

i = 0;
repeat
  print(i);
  i = i + 1;
until i > 0;

This ensures that the code inside the repeat/until block executes at least once, even if the value of the expression, i > 0 in this case, is false the first time through (e.g., if you set i = 25 to start).

In all cases, the break statement is available to break out of the innermost-enclosing loop unconditionally. You can also terminate any loop by returning it from it explicitly.

Compartmentalizing: Modules

When you write a Corona app, it all starts with main.lua. You could, if you wanted, write an entire game in just that file! There’s no real limitations, other than your sanity and the performance of your IDE, that would stop you from doing this. However, from an architectural standpoint it’s not generally a great idea to do this in anything but very trivial cases.

Lua provides the notion of modules to help you better organize your code. A module really is nothing more than other Lua source code files that you include into your main.lua file by way of the require() statement. So, say you have main.lua and you also have myFunctions.lua. You would write in main.lua:

require("myFunctions");

Note that we do not need to include the file extension here. The interpreter knows you mean a Lua source file after all!

Now, let’s say that myFunctions.lua contains the following:

function testMe()
  print("hello!");
end

In main.lua we can call testMe() and have it print its greeting to the console as expected.

Now, since clogging up global scope is generally a bad idea (and in point of fact can lead to performance degradation owing to scope chain lookup), the more common thing to do in myFunctions.lua is this:

return {
  testMe = function()
    print("hello!");
  end
};

Now, in main.lua we do:

myFuncs = require("myFunctions");
myFuncs.testMe();

Returning a table is said by many to be what makes myFunctions.lua a “proper” module in Lua. If it just contains global functions, it isn’t generally thought of as a module. Whatever terminology you choose to use, the point is that require() allows you to bring in external code into your Lua program—that’s the important concept. Most of the time, however, you’ll see in the Corona API that you do in fact get a table back from its execution, and you generally should follow this pattern in your own code if for no other reason than to pollute the global namespace as little as possible.

Two other important notes about modules is that, first, anything not declared in global scope in a module is scoped to the module itself. So, if myFunctions.lua includes this line:

local myVar = "test";

then that variable myVar will only be accessible from code inside that module.

This is important in light of the second fact, which is that modules are only loaded once per program execution (unless you go out of your way to defeat this mechanism).

So, let’s say you write yourself a utils.lua module to include some general utility functions you’ve written. Being a good module, it returns a table. In main.lua you require module1.lua, which requires utils.lua and assigns the table it returns to a local utils variable. This utils variable is only accessible to the code in module1.lua since it is declared as local.

Now, let’s say you add module2.lua later on. It too needs access to the utility functions. At this point, you have a choice. Your first option is to move the require("utils") from module1.lua into main.lua, which makes it global (assuming you don’t define it with local, which would then scope it to the code in main.lua). The other option, and generally the better one because it avoids extended scope chain lookup, is to also require utils.lua in module2.lua and assign it to a local utils variable there too. Remember, module1.lua and module2.lua are two separate scopes, two separate namespaces if you will, so there is no conflict with having two utils variables defined.

However, the important fact is that utils.lua will not be loaded a second time when required in module2.lua. The Lua interpreter recognizes that utils.lua has already been loaded and so returns the existing reference. This can have consequences if there is, for example, some setup code in a module that may need to be different when it is included from two separate modules.

The easy answer is that your modules shouldn’t execute any code when loaded. Since Lua will interpret the included Lua source file top to bottom, if all you have is a table definition with properties and methods defined then nothing will execute per se at load time, other than the return statement sending the table back to the caller. That way, there is no side effect to loading a module, and you can include it as many times as you want without worrying about anything breaking.

Variations on a Theme: Changes Made to Lua in Corona

The version of Lua in Corona has had some fairly minor alterations made to it to work better with the SDK for improved function on mobile devices. The big difference is that you are unable to load Lua code dynamically and execute it. I suspect this has a lot to do with the restrictions Apple places on apps on iOS devices, but regardless of the reason, this means that the following four Lua functions which otherwise would be available to you are not: dofile(), load(), loadfile(), and loadstring().

As a consequence of those functions not being available, you will not be able to use Lua code as a configuration file, as is a common practice in Lua development. You’ll either have to store your Lua configuration in an existing Lua file or in some other format.

Summary

In this chapter, you made a new friend: Lua! You saw how this relatively simple language (from a syntax perspective) provides the semantic framework you’ll need to make use of Corona. You saw how the usual constructs of programming languages such as variables, data types, loops, control statements, and objects are dealt with. You’ve built the foundation you need to continue on into the brave new world of Corona itself.

In Chapter 3, you’ll be taking your first steps into that world and toward building an actual game with Corona, using the tools gained here and in Chapter 1.

In brief: the setting of the stage is now complete and it’s time for the fun to begin!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.253.62