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 .... endconstitute chunks, as do conditional statements
if ... then .... else ... endFor 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 scopeHere 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 scopeIn 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 endis 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 endas 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 endwhich 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 0There 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: xThe 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 = 1and in the second
start = 0, increment = 10So 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