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
@foo
where there is only onefoo
will 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
foo
or removing one from an object will break all references to existingfoo
resp. 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.