No idea how to write a good title for this question.
I'm thinking about introducing an operator in a DSL that makes hidden identifiers (like variables) accessible. Think this.foo in Java or C# to access a member that was hidden by a local foo. Or base.bar when bar is a member in the current class. Let's call this "explicitly accessing an enclosing scope".
Target audience of the DSL are non-programmers / novices, which should not be required to understand (in detail) the scopes they're working in. So the idea is to instead introduce an operator that skips one occurrence of an identifier and can be applied multiple times. The compiler will do his usual thing when resolving identifiers, starting in the local scope and moving outward. When he finds something and the operator is used, he ignores the result and keeps on looking.
Consider this pseudo-code example:
foo = 1 // global
object bar {
foo = "member foo"
function bla() {
let foo = 2
// _ we are here
}
}
In the target location, foo refers to the local variable with value 2.
@foo will be the member of type string (skip one occurrence)
@@foo will be the global variable with value 1 (skip two)
The alternatives in languages like Java or C# would be this.foo for the member and some imaginary global::foo for the imaginary global. The problem with that is a) users must learn several keywords and b) users must understand the exact structure of scopes (what does this refer to etc.)
The actual question: is this a bad idea, are there some severe problems I'm not seeing?
Or are there perhaps any languages that employ such an operator that I could look into?
A bit more context that may be relevant:
- this is not a general-purpose programming language
- it's a mix of textual and visual elements (think Excel), so it's clear visually what is contained in what (and where skipping occurrences will take us)
- static typing, no dynamic scope (evaluation at compile time)
- The idea is that people "get" that their thing has the same name as a different thing, but they don't know the magic words to explicitly refer to that thing's container.
- writing
@foowhere there is only onefoowill result in an error
Update: A few observations:
- Lexical Scopes: I failed to mention this, but the operator would escape all lexical scopes when used first. If there are 3 variables in 3 nested blocks, like loops or functions, you cannot use
@to address them. It is only intended to access non-lexical scopes (objects, global scope). I believe lexical scopes are even more difficult to understand for novices and thus they wouldn't understand how many@they'd need to address a member. - Brittleness: As John R. Strohm mentions, introducing a new
fooor removing one from an object will break all references to existingfooresp. the number of@required to address them will change. That is indeed a problem, but isn't that the case in e.g. C# as well? Consider this example:
.
namespace foo {
class X {
public static int Value;
}
namespace bar {
class foo { }
class X {}
class Main {
public static int Value = global::foo.X.Value + 1;
}
}
}
By creating a new class users can hide other types and namespaces in outer scopes. Existing code will break and must be updated, either by prefixing namespace names or global:: if namespaces were hidden.
The point is: this problem exists in languages like C# and doesn't cause problems. The problem is more severe with the @ operator because removing things will also cause it to fail, whereas explicitly addressing scopes fixes the problem once and for all. My argument to that is this: when you have to fix your code when adding stuff, it can be expected that you also have to fix it when removing stuff.
Update: I accepted John's answer because he points out a problem that I believe is worse than the one most others mentioned (copying code to a different location may alter which symbols are being addressed).
The problem is that if a user creates a new symbol foo in one layer he alters which foo symbols are resolved in nested layers. In other words an invisible change to code that is not currently being edited. In my opinion this is worse than the copy-paste problem, as that can at least be dealt with right then and there and while it still my go unnoticed, the user has a greater chance of discovering it.