lua-users home
lua-l archive

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


On 2/23/08, Roberto Ierusalimschy <roberto@inf.puc-rio.br> wrote:
>  Maybe a (partial?) solution to the implicit global problem would be to
>  follow the style of strict.lua, but at compile time. There would be
>  only one rule:
>
>  - an assignment to an "unseen" global inside a function raises a
>  compile-time error.

I know that this proposal has been well received, but I just can't
shake the feeling that the behavior here is too magical to belong in
the core.  I feel this strongly enough to chime in and try to make the
point.

>  Otherwise you may use something like "name = name". It is
>  strange, but it is (or should be) uncommon for a function to mess with
>  globals declared elsewhere.

I've been mulling over this proposal for a while now, and I'm pretty
sure this is where my problem with it lies.  It just seems to me that
there are legitimate reasons for functions in different chunks to
modify the same global.  (Perhaps we want a global counter of a
certain event, and we want to increment it from various functions
defined in different files.)  The syntax "foo = foo" to enable this
seems to have several strong points against it.

1) It is entirely non-obvious syntax.  It's hard to imagine
programmers coming from other languages guessing that "foo = foo" at
chunk scope means "let the functions below modify foo".  Novice
programmers will never be able to guess this.

2) Once explained to users, it's still unclear why "foo = foo" should
work at certain scopes and not at others.  It's easy enough to
document that "foo = foo" doesn't work inside functions, but I don't
think it's clear why this should be so.

3) As Jerome Vuarand has pointed out, "foo = foo" already has a
meaning, and it's not necessarily a no-op!  It's entirely expected for
programs to provide custom global environments with metatables that do
interesting things, right?  So global assignment today can have all
kinds of interesting side effects.  Compile-time strict.lua-style
checking makes it *impossible* for a function to invoke these side
effects unless the containing chunk has invoked them first!

Although I raise these objections, I do like strict.lua, because it
solves a real problem and it demonstrates how extensible Lua is.  None
of the above problems are a big deal with the strict module; #1 and #2
are acceptable compromises for a module of this type, and you would
never use strict.lua if #3 was a problem to you.

But as a core language feature, these inelegances should be carefully
considered.

I have to agree with Miles Bader's sentiment that the attempts to give
Lua implicit locals are misguided.  I also agree that, if implicit
globals indeed cause a problem that needs to be fixed at compile time,
then it should be done with an explicit declaration -- that is, a new
keyword.  I know that adding a new keyword breaks existing code, but
any change that turns previously legal assignments into syntax errors
is guilty of the same thing!

Since we're discussing specific solutions here, I'll propose my own
solution, even though I know talk is cheap and my solution isn't
perfect:

Add a new keyword 'global' which can be used as a statement at any
scope.  It can be followed by a comma-separated list of names, in
which case those names refer to the corresponding globals for the
remainder of that scope.  Global declarations shadow outer local
declarations and vice versa.

Reading a name with no declaration of course still accesses the global
scope, but attempting to assign to an undeclared variable is an error.
 The statement "global *" changes this behavior -- for the rest of the
block containing "global *", writes to undeclared variables affect the
global scope.  The interpreter loop prepends "global *" to all
interactive input, and all chunks compile as though "global *" were
present when LUA_COMPAT_GLOBALS is defined.

'global' also has all the convenience syntax that 'local' has, so that
you can say, for instance, "global foo, bar = 1, 2" or "global
function baz() end".

I know that this counter-proposal is pretty heavyweight, and results
in a language that doesn't quite feel the same.  I'm not entirely sure
if this is good or not, but I am pretty sure that if global assignment
really is broken today, the correct solution should look something
like this.

Greg F