Scope

A Lua program consists of chunks of code. Chunks can contain subchunks. A chunk can even reside in a separate file. Chunks are important because they are regions of code in which a local variable has its scope. When a variable in Lua is declared local its scope is what follows the declaration within the smallest chunk in which the declaration appears, including any of its subchunks.

In BASIC scope is less important, because there are only two kinds possible: global scope, for variables defined in the main part of the program, and local scope for variables declared local in a function or procedure definition. So the tree of scopes in BASIC is very simple. There is the root of the tree, for global scope, and a leaf for each function or procedure definition; they are all subchunks of the global chunk at depth 1 because function/procedure definitions cannot be contained within other function/procedure definitions.

In Lua the tree of scopes can be as general as you like; chunks can be nested to arbitrary depths. Furthermore Lua has many more sorts of chunk. All the looping constructs

    while ... end
    repeat ... until ...
    for ... do .... end
constitute chunks, as do conditional statements

    if ... then .... else ... end
For chunks are compiled as if they were functions of the iteration variables, which are in consequence local to the chunk. Otherwise non-function chunks are compiled as if they were functions of no arguments. Note how the keyword end does frequent service marking the end of many constructs.

     for i = 1, 10 do f (i) end
     print (i) --> nil because i is out of scope
Here nil is a special value that variables have by default if they are undefined. Assigning nil to a variable effectively undefines it, and any memory used for storing its previous value becomes garbage-collectible. By contrast in BASIC

     FOR I = 1 TO 10 PROCf(I) NEXT
     PRINT (I)  REM 10 because I has global scope
In Lua you can make your own chunks by enclosing code within the do and end keywords. C has something similar using braces.

RiscLua introduces two abbreviations. The keyword function can be replaced by a backslash, \ . The keyword return can be replaced by =>. Functions are first-class citizens in Lua; they are values like any other. There are no special function-definitions. So

        double = \ (x) => 2*x end
is just an assignment. Functions constitute chunks, and formal parameters are automatically local to them. It is true that Lua offers an alternative syntax

         function double (x) return 2*x end
as a comfort-blanket to those who cannot wean themselves off first-order languages, but I prefer not to use it. A first-order language, like BASIC or C, is one that distinguishes passive data, such as numbers or strings, from functions that transform data to data. By contrast, a higher-order language, like Scheme or Lua, makes no such distinction. Higher-order languages are more expressive. Consider, for example,

     curry = \ (f) => \ (x) => \ (y) => f (x, y) end end end
which transforms functions of two variables into a function of the first returning a function of the second.

The following code creates counters, a counter being a function of no arguments which returns the next value in an arithmetic sequence every time it is called. We see three levels of scope.

                                               -- level 0
  local make_counter = \ (start, increment)    -- level 1
        local count = start                    -- level 1
        => \ ( )                               -- level 2
           local x = count                     -- level 2
           count + = increment                 -- level 2
           => x                                -- level 2
           end                                 -- level 2
        end                                    -- level 1
  local counter_a = make_counter (1, 1)        -- level 0
  local counter_b = make_counter (0, 10)       -- level 0
There is the outer chunk at level 0. In it there is a function chunk, that starts with \ , giving level 1, and within that another function chunk, that starts with \ , giving level 2. What are the local variables in each chunk?

At level 0: make_counter, counter_a, counter_b.
At level 1: start, increment, count.
At level 2: x
The variables count and increment inside the level 2 function chunk are called upvalues. An upvalue in a chunk is just a variable local to a surrounding chunk. The two counters, counter_a and counter_b, are closures. They share the same code, given by the function in level 2, but they use different environments. In the first case the environment

     start = 1, increment = 1
and in the second

     start = 0, increment = 10
So a closure has two ingredients: a function, and an environment which assigns values to the upvalues in the function, without which the function cannot be evaluated. First-order languages do not need the environment ingredient because, without nesting of non-global scopes, they cannot have upvalues. This simplification makes them easier to implement, but less expressive. So these ideas (closures, upvalues, chunks) may well be daunting initially for those new to Lua, but they are important and repay study. In reading a Lua program you should aim to be able to
  • identify the chunks and their tree structure,
  • for each chunk identify its local variables,
  • for each chunk identify its upvalues.