Lua Scoping Discussion |
|
VersionNotice: Comments on this page refer to a quite old version of Lua (2001 era) that had more limited lexical scoping and presumably non-complete closure support. Many of these comments don't apply on recent versions of Lua.
Lua is statically scoped [1].
There is some disagreement as to whether this situation should be called limited/broken static/lexical scoping. Some computer language texts define lexical and static scoping as being the same, confusing the matter. Lua does act as lexically scoped when the variable accessed is at the global scope:
do -- assume "a" is a global with value 5 local foo = function() print(a) end a = 10 foo() -- prints "10" end
It also acts as lexically scoped when upvalues are used to access the next outer scope, as long as the variables are not rebound (either from the variable's scope or from the nested function):
do local a = 5 local foo = function() print(%a) end foo() -- prints "5" end
In any case Lua is perhaps not as bad off as other scripting languages such as Perl when they were young. Perl started with dynamic scoping. It has slowly evolved to support lexical scoping via the my
qualifier. Lexical scoping may become default in Perl 6.
Python previously had limited static scoping similar to Lua. Lexical scoping will become the default in 2.2 and is already an option in 2.1 [2]. Python's implementation of lexical scoping does not allow rebinding of variables from inside a nested function, which is similar to Lua's upvalues.
For Lua, maybe all that is needed is to expand its limited subset into true lexical scoping. It could continue the upvalue concept and not allow rebinding as in Python. Or it may allow rebinding and eliminate the need for upvalues which are often a source of confusion for Lua programmers. In the design document for Python's lexical scoping, the main reason cited for not allowing rebinding was Python's lack of variable declarations. Since Lua requires local
to qualify variables this would not be an issue.
-- E. Toernig
From page 411 of "Compilers, Principles, Techniques, and Tools", by Aho, Sethi, and Ullman, 1986, Addison-Wesley:
Comparing the FOLDOC entry with what's in the Dragon Book, the latter's definition seems a bit looser in that the "smallest block" stipulation is not made essential and is said only to be a property of certain languages such as C. In other words it is enough that a given scoping rule not be dynamic (that is, not dependent on current activations) to be considered static.
In the Lua language, each variable has either local or global scope. Functions can be defined within functions, however, a nested function does not have access to variables defined in any of its enclosing functions. Therefore, Lua variables lack static nested scoping. As result, the following Lua code is illegal:
function addn(x) function sum(y) return x+y end return sum end print((addn(3))(1))
This example is illegal because variable x defined in addn is inaccessible to the sum nested function.
Nested functions can get access to a copy of a variable defined in its immediately enclosing function. An upvalue reference to a variable within a function extracts this copy when the function is evaluated to produce a closure. A variable reference preceded by a percent sign signifies an upvalue reference. Placing a percent sign in front of the x reference in sum makes the above example legal Lua code.
This poor substitute for static nested scoping has been adopted because it makes it easy to produce an implementation in which all non-global variables are stack allocated, however, one need not give up static nested scoping to have stack allocated non-globals.
The Python language previous to version 2.2 had scoping rules similar to Lua's current rules--each variable had either local or global scope. As of version 2.2, Python has static nested scoping. In Python's implementation, non-global variables referenced from a nested function are immutable. With this extra assumption, stack allocated non-globals is easily implemented.
Python's implementation demonstrates the validity of this approach. They have a fast implementation as a result of using flat closures, as described in 1984 by Luca Cardelli in a paper called "Compiling a Functional Language"[3]. The relevant section is 4 on "Fetching variables".
For those of us who hope that future versions of Lua will have variables that have static nested scope, I suggest we can help by finding implementation techniques that can be used by the core Lua implementors. I think we should carefully study the relevant sections of the Python 2.2 implementation for ideas, and make them available.
I believe programs written in a good very high-level language should be easily read by people only casually familiar with the language. With the exception of upvalues, I think Lua excels in this respect as it adopts Pascal-like syntax for control structures whose meaning is just what one would expect. Programs that employ upvalues are unlikely to be understood by casual users. One needs a manual to understand when they get their value. Most people intuitively understand static nested scoping. Making variables immutable when they are referenced from within a nested function puts a burden on the authors of a Lua program, but not on readers of the program. I think Lua should be designed to meet the needs of readers foremost.
Here is a definition of static nested scoping:
-- John D. Ramsdell