lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


> Lua is great on almost every point, except the scoping rules. Why can't we
> have normal scoping rules (i.e a variable is local unless defined
> globally?).

What is a "normal" scoping rule? In many languages (AWK, Snobol, Perl, etc) 
a variable is global unless declared local. Most other languages (C, C++, 
Pascal and its heirs, Haskell, Lisp, etc) do need global and local 
declarations, but we usually only care for the locals when reading a 
program. In C, for instance, when you read inside a function something like 

  varname = 1;

you do not go all over the file (and includes) to see whether "varname" is 
declared as global. You just check if varname is declared local; if it's 
not, then it is a global variable. A compiler does the same, and only 
*after* seeing that the variable is not local it will need a global 
declaration. 

Very few languages do create local variables without some kind of explicit 
declarations. This is because local variables are local to a given scope, 
and so we must specify this scope. If we get the rule that "a variable is 
local unless defined globally", we will have some weird behaviours; for 
instance, in Lua (and also in C and Java) the scope of a variable is not a 
whole function, but the inest block where the variable is declared. So, if 
you write something like 

  if a then i=1 else ... end

the scope of the variable 'i' (now local by default) would be only the 
"then" part of the if!! (the inner block...) 

In Lua you do not need global declarations. You can complain about this 
(and many people do); but to assume that a variable without this global 
declaration should be automatically considered local does not look "normal" 
to most languages, and certanly is not the right way to solve the problem 
with undeclared global variables. 

If the real problem is the use of global variables without declaration, 
there is an easy and efficient way to solve that, using tag methods. The 
idea is to set the tag methods 'getglobal' and 'setglobal' over the tag nil 
(which is the value of any "undeclared" variable), and to keep a table with 
the name of all declared global variables. When you read or write a nil 
global variable, the tag method goes to the table to check whether the 
variable has been declared. When you read or write a non-nil global, the 
most frequent case, there is no cost at all. 

==================================================================
_Globals = {}   -- a set to keep the names of global variables

function declare (globalname)
  _Globals[globalname] = 1
end

function undeclare (globalname)   -- optional
  rawsetglobal(globalname, nil)
  _Globals[globalname] = nil
end


settagmethod(tag(nil), 'setglobal', function (name, oldvalue, newvalue)
  if not _Globals[name] then
    error("cannot write undeclared variable `"..name.."'")
  end
  return rawsetglobal(name, newvalue)
end)

settagmethod(tag(nil), 'getglobal', function (name, value)
  if not _Globals[name] then
    error("cannot read undeclared variable `"..name.."'")
  end
  return value   -- or nil (value must be nil...)
end)

==================================================================
Example:

> dofile'temp'   -- the above code
> print(i)
lua: cannot read undeclared variable `i'
Active Stack:
        function error [in file (C)]
        `getglobal' tag method [in file temp]
        main of (dostring) >> "print(i)"
> declare'i'
> print(i)
nil     
> i=10
> print(i)
10      
> a = 10
lua: cannot write undeclared variable `a'
Active Stack:
        function error [in file (C)]
        `setglobal' tag method [in file temp]
        main of (dostring) >> "a = 10"
> undeclare'i'
> i='hello'
lua: cannot write undeclared variable `i'
Active Stack:
        function error [in file (C)]
        `setglobal' tag method [in file temp]
        main of (dostring) >> "i='hello'"


-- Roberto