Last time I proposed "$" prefix because some programming languages (Perl, bash, PHP) have variable names prefixed with "$".
But it seems some people dislike any similarities with Perl :-)
"$" was also criticized because it requires pressing SHIFT on a keyboard, and programmers are usually lazy (laziness is the first great virtue of a programmer, according to Larry Wall)
Ok, let's stop talking about Perl :-)
This time I show another possible prefix: the backslash.
Backslash is not used in Lua syntax (except inside quoted strings), so why not?
Anyway, the choice of prefix ("$", "@", "\",...) or postfix or another syntactical way is pure aesthetic/bikeshedding; it doesn't actually affect my suggestion.
My suggestion is the following:
A programmer must explicitly (by means of special syntax) say to compiler whether each variable reference is global or not. The following examples assume globals are prefixed with backslash, and locals/upvalues are not prefixed:
print"Hello" -- error: unknown local/upvalue "print"
\print"Hello" -- ok
function print() -- error: unknown local/upvalue "print"
end
function \print() -- ok
end
local func1
function func1() -- ok
end
local function \func2() -- error: global identifier in local definition
end
function math.atan2 (y, x) -- error: unknown local/upvalue "math"
end
function \math.atan2 (y, x) -- ok
end
\math.atan2 = \math.atan -- ok
local \bit32 = {} -- error: global identifier in local definition
local function func3( \x ) -- error: global identifier in local definition
end
Q#1:
How this would be useful?
A:
1) All typos in locals/upvalues would be detected at compile time.
2) Globals would never be occasionally shadowed out by locals.
Q#2:
I want to see how my old scripts would look in globals-backslashed style.
A:
There is a convertor from "source text" to "globals-backslashed source text":
https://gist.github.com/Egor-Skriptunoff/c2d1bd8b5348ab3ed59991a9dda7ed5c
Q#3:
This suggestion may be useful only for those who write long Lua scripts where locals/upvalues are accessed much more frequently than globals.
But I use Lua for writing config/embedded/DSL scripts (short scripts working with API exposed to Lua as global variables/functions), so I almost never use local variables.
Or maybe I simply want to write small quick-and-dirty Lua program to execute it only once in my life.
Or probably I use Lua in REPL sessions.
The "globals-backslashed" suggestion would inconvenience me.
A:
Globals-backslashed style should be optional.
Lua should accept both formats of source text: old style and globals-backslashed style.
I suggest both these formats to coexist in all future versions of Lua.
Support of old style should never be dropped.
So, if you don't have benefits from globals-backslashed style - just don't use it.
We already have two formats of Lua chunks: "source text" and "bytecode".
There are absolutely no problems with mixing different formats in a single project: "source text" chunks can require/dofile/dostring "bytecode" chunks and vice versa.
Actually, I suggest to add the third format: "globals-backslashed source text".
The chunk loader (load/lua_load) can auto-detect new type of chunk: if chunk doesn't start with "\27" and does contain at least one identifier prefixed with backslash then it's of type "globals-backslashed source text".
This way, introducing of globals-backslashed style would not break compatibility with existing code (and with existing habits of Lua users).
Q#4:
Now whenever I look at Lua code the first thing I have to do is figure out if it is genuine Lua, or backslashed-Lua.
A:
If your eyes made incorrect decision about whether the source is genuine or backslashed, and you add some modifications to this source, you would be informed at compile time.
This mistake would never go to runtime.
Anyway, you could convert any script to your favorite style before modifying it.
Q#5:
Is it natural to introduce syntactical difference in Lua between globals access and locals access?
A:
Global objects are usually either Lua standard libraries or API provided by host application.
In other words, globals are created not by you, semantics of global objects is fixed and documented.
On the opposite, locals/upvalues in your scripts are created by you, their semantic is 100% under your control.
This semantical gap between globals and locals deserves explicit syntactical distinction.
The other reason why globals are special:
Globals have very wide scope (all the files in your project), so values of globals may be modified by external functions invoked from your script.
On the opposite, locals/upvalues are always under control of your script.
So, the leading backslash acts like warning indicator: "be careful, this variable is not under your control, it may have changed its value inexpectingly"
For example, this syntactical warning would help you to pay more attention to compatibility of your script with monkey-patching.
Q#6:
To solve the problem of typos Lua already does provide some abilities such as setting metatable on _G or setting _ENV=nil.
A:
The only thing we could do in Lua 5.3 is various forms of "runtime alarmer".
A runtime alarmer defers error detection to runtime (which is bad) and couldn't guarantee 100% coverage (what about typos in inactive "if" branches?).
IMO, the problem "the compiler silently ignores all variable name typos" does worth introducing some minor changes in the language to solve it.
It's the expectation of many programmers that a compiler should check for misspelled identifiers and warn the user at compile time.
The silence of the compiler is "just an invitation to bugs due to spelling mistakes", as Axel said.
Q#7:
This is not really necessary, just use luacheck systematically to find typos and inadvertant globals.
Or use special IDE (ZBS) that highlights all global access.
Or write your own static analyzer that get pedantic about unknown global access.
A:
IMO checking for misspelled variable names is quite important to be inside Lua core.
Q#8:
The distinction between compile-time and run-time is illusory in any interpreted language that offers a function like "load".
A:
Most of mistakes are located in usual hand-written code which is accessible for static code analysis.
Scripts being generated on-the-fly are definitely not the main subject to linting.
But anyway, globals-backslasing-style could be used in "load"-ed code to help reducing error-detection-delay.
Q#9:
I am not a believer in Hungarian notation. Making it part of any language is the silliest thing I could think about.
A:
The "Hungarian notation" is just an instrument, it is not a subject to believe or afraid of.
Actually, "Hungarian notation" is one of possible ways to split namespace.
Splitting a namespace might be good or bad, it depends.
IMO, splitting variable namespace into two: globals and locals, would be good in Lua.
Q#10:
Understanding and debugging someone else's code is a PITA in any language, period.
No amount of special symbols on global variables is going to change that.
A:
IMO, splitting variable namespace would help to make understanding of other people's code a bit easier.
Q#11:
Give me as few restrictions as possible on identifiers and if I feel I need a naming scheme, I can create one myself.
A:
No, you can't, because the compiler wouldn't be aware of the naming scheme "created" by you.
For example, if globals in your naming scheme are started with "g_", the compiler wouldn't be able to find your mistake in "local g_var" (global identifier is used for local var).
The compiler also wouldn't be able to detect misspelled local variable name due to "globals-by-default" rule is active despite of your imaginary namespace.
You do need the assistance of the compiler. That means you need special syntax.
Q#12:
There is another way to make global access explicit: declaring necessery globals with new statement: "global table, math, print"
A:
I don't like the idea that the line where global identifier is used may be far away from the line where it is declared.
Prefixing is more simple, obvious and easy-to-read way.
Prefixing also solves the problem of globals accidentally shadowed out by locals with the same name.
Q#13:
What modifications should be done in Lua core to implement globals-backslashed style?
A:
The modifications required are surprisingly small enough (for an experimental feature of Lua):
1) Backslash-prefixed variable reference should be converted to a field of _ENV table.
2) Variable reference without backslash should generate an error if there is no such local/upvalue.
3) Auto-detection of "globals-backslashed source text" chunk format actually doesn't require two-pass compiler.
If the format of current chunk is not known yet, and the first global variable reference whithout a backslash is occured, a deferred error should be created whithout stopping the compilation.
When first identifier with a backslash is occured (this means the chunk format is detected now: "globals-backslashed"), the error previously saved should be generated.