This post was originally published at Tutorial: Scope for beginners

As a new (or relatively new) Lua/Corona programmer, are you confused by errors like this?

If so, then you need to learn a very important programming concept known as scope. Scope impacts Lua’s ability to “see” variables (as in the error above) as well your ability to manage where functions exist in relation to other functions and variables.

In a previous tutorial, we learned the importance of well-formatted code and the use of descriptive variable names. Another topic discussed was how to keep functions manageable. When a function gets too long, it can become hard to read, and one reason why a function may get really long is that it has other functions nested within it. While there are various practical reasons to nest functions within another function, generally it occurs because of the concept of scope.

Scope defined

When you think of terms like “telescope” or “microscope,” you think of devices that help you see, and see things within a limit — for instance, very small things in a microscope. In programming terms, scope is used to define what parts of your code can “see” variables, objects, and functions that you have created. In Lua, there are two main scope controls: global and local.

Global

Global variables/functions can be seen anywhere in your program. “Great, I’ll use global scope for everything!” you might say, but global objects are actually quite bad and you must understand their limitations and weaknesses. In fact, new programmers should avoid using them. One reason why globals are bad is that — because they have no visibility limits — they are expensive to use in regards to performance. They also have the inherent risk that, if you assigned a global in one module, you may accidentally create another of the same name in another module, thus “trashing” the previous one.

We discussed the global scope in more detail in the Goodbye Globals tutorial, along with a method to create “global-like” (but not truly global) objects.

Local

In Lua, the preferred scope is local which you control by using the local declaration before you define a variable/function. For example:

When you prefix a variable or function with local when you first create it, it becomes available to that block of code and any “children” blocks contained within (if you are not familiar with the concept of blocks, please read the previous tutorial). Consider this example:

In this case, the function addTwoNumbers() defines a new block of code. Inside this block, we create a new variable named sum. This variable has the local keyword, meaning it’s only visible to the function addTwoNumbers(). No other code in this module or overall project will be able to “see” the sum variable. Thus, when the function ends, there is no longer a variable known as sum and nil will be printed.

Now consider this block:

Like the first example, sum is local to addTwoNumbers(). That lets us add two numbers, and it’s also visible inside the block started by if sum < 10 then. However, inside of that if-then block, another local variable (secondSum) is created, and that variable becomes local only to the if-then block. So, the attempt to print secondSum outside of the if-then block will result in nil, since secondSum doesn’t exist at that point in execution.

Functions inside of other functions

Because new programmers struggle with scope, event-driven libraries like Composer can create extraordinary stress in managing code. For new developers, it’s challenging to understand the basics of when scene:create() is called, when its work is done, and when scene:show() is called.

One approach that some beginners follow is to place child functions inside of the scene:create() function — or worse, move all of the scene creation code to the scene:show() function since they can’t get scope to work between the two. In a typical scene, you may need touch handlers for objects, event handlers for buttons, and/or collision handlers to detect when objects touch each other. In turn, these functions may call other functions, and if you put all of those into either scene:create() or scene:show(), that function will become very long and hard to manage.

One common scope problem is declaring things as local inside of scene:create() when you may need to access to them elsewhere. Consider this example Composer scene:

Let’s start by analyzing the scene. Visually, it will have a background, a text object to display the current score, and a button to increment the score.

Following the standard Composer concept, we create all parts of the user interface (UI) inside of scene:create(). The first thing we create is the background. For this simple scene (and in many scenes), the background object won’t need to be accessed after it’s created, so we define it locally within the scene:create() function. However, the variable to keep track of the score (score) and the text object to display that score (scoreTextwill need to be accessible (seen) elsewhere in the scene code — thus, we define them near the top of the module and not inside another function or block:

Note that the variables are not initialized with any values, but rather they are simply declared. Not until scene:create() is the text object actually created and assigned to the variable scoreText.

For the widget button that increments the score, we don’t need to access it after it’s first created, however it does require an onEvent handler function (addToScoreButtonEvent). This is an instance where it would be perfectly fine to create that function directly above the widget.newButton() setup code. That would keep it closer to the button code, but it would make the scene:create() function longer too. So, for clarity in the block, we’ve written that function above and outside the create function. As for the function itself, it does two things: it adds 1 to the score, then it updates the scoreText object.

Finally, let’s inspect the scene:show() function. In this function’s "will" phase, we initialize variables that we want to reset when we re-enter the scene (the scene:show() function will be called every time the scene is presented on screen). In this case, we initialize score to 0 and draw the first value for scoreText. Again, if we had made these variables local inside of scene:create(), the scene:show() function would not have access to them.

The power of the indent

In the previous tutorial, we talked about how important it is to indent your code for readability. Indentation helps with scope as well! When you indent each block properly and you use a local keyword, that variable can’t be seen anywhere left of that block. Its a good visual clue to where your variables are visible.

Index variables in “for” loops

When you write a block of code like:

the variable i is local to the for loop only. When the loop ends, i will forget its final value in the loop and return to it’s previous definition (usually nil). In the example above, a local variable i was set to 999 before the for and while the loop executes, it’s value represent the values 1-10. When the loop is done, the i that is local to the loop goes away and the previous i‘s value of 999 is now the visible value.

Conclusion

Hopefully, this tutorial has illustrated that scope as an overall concept is relatively straightforward. When you use the keyword local, the variable can only be accessed at that block level or within any child blocks. With that in mind, if your variables or functions need to be seen at a wider level, simply move them above/outside the block or define them near the top of the module.


See the article here: 

This post was originally published at Tutorial: Scope for beginners