-
Notifications
You must be signed in to change notification settings - Fork 214
Importing name spaces locally #267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
My proposal does not make it an error, because the following is not an error: class A {
int x;
}
String x;
class B extends A {
// A plain `x` is resolved lexically, that is, it denotes the top-level variable.
String s = x; // No problem with access to `this`, because we don't; so the type is OK.
String get foo => x; // Returns the value of the top-level variable, so the type is OK.
} So we're using exactly the same approach to implicit "member" access with a locally imported name as we are with It seems un-Dartish to me to use different rules for The underlying rationale is that the lexical scope is more accessible to a person who is reading the code than the declarations of features in superinterfaces of the current class, so the lexical scope wins if there is a declaration. Also, (assuming that we'd use the opposite priority) you could have a superinterface which is maintained by some third party that you do not communicate with frequently, so they could introduce an extra name in one of your superinterfaces, and you might not notice. It might break your code (say, if your class is concrete you might now need to write an implementation of one more method), but if it doesn't directly break your code then it could change the meaning of a plain identifier (like Another thing to note is that the third party change in a superinterface could break your code such that you have to edit it if the superclass gets the higher priority, and this will not happen when it is the lexical scope that gets the higher priority. Conversely, with the current choice (lex wins), a third party library that you're importing without a prefix could start exporting one more name, and that could break your code (because that So this prioritization of the lexical scope was not made by accident. I don't know how many conflicts we would actually have if we were to make all these situations an error (rather than resolving them in favor of the lexical scope). We could have a lint that checks all identifiers and reports all those that have this kind of ambiguity, but it seems likely that this is such a deep property of Dart that it would be a hugely breaking change. |
That's an error: With multiple local imports offering the same name, there is no error at the imports but every use of the ambiguous name is an error. So you'll have to use a prefix like We could say that "imported symbol never wins", but that's the rule that we already have! —that is, if you're willing to accept the notion that the treatment of |
I chose I considered allowing local imports of the form |
Uh oh!
There was an error while loading. Please reload this page.
In response to #266, this issue is a proposal to add support for locally importing global and static name spaces as well as objects in a function body. It is an enhancement of the mechanism associated with
this
in instance methods, and the import mechanism of libraries.The purpose of this issue is (1) to be a reminder about certain features that the language team has discussed, and (2) to put the
this
binding feature in anonymous methods (#260) in perspective. If we wish to be very orthogonal, we could consider thethis
binding in #260 as syntactic sugar for a local import.The local import feature proposed here is similar to the
open
construct in SML (see, e.g., p47 of this PDF), except that it does not cause an error to import the same name from more than one source, an error arises only if that name is used.Introduction
The mechanism proposed here is simply to enable
import
clauses to apply to library prefixes, enumerations, and objects, and to occur in function bodies. The effect of doing this is that the code can be more concise, without polluting the global name space.In other words, one attractive property of this feature is that it lowers the cost of keeping the name space small and comprehensible at the top level (say, by importing with a prefix more frequently), because it enables each function body to open whatever name spaces it uses frequently.
For example:
could be expressed using local imports as follows:
In all cases, the scope rules would be the same as the ones that we currently use for implicit member access to the "current" instance of an enclosing class: If an identifier reference
id
is not declared in an enclosing scope then it is treated asthis.id
.Note that this rule is applicable for any identifier reference which is the first part of an expression, e.g.,
foo(42)..bar[3] = 14
meansthis.foo(42)..bar[3] = 14
iffoo
is not declared in an enclosing scope. Subsequent static analysis may find thatthis.foo(42)..bar[3] = 14
is a compile-time error, but it would already have been an error if considered asfoo(42)..bar[3] = 14
.In other words, a local import will never change the meaning of any expression which would not be an error if the import were removed. This is important, because it allows developers to rely on understanding the meaning of an already-meaningful expression, without ever taking the detour into considerations about implicit usages of
this
, or any other locally imported name space.One existing SDK issue which would be addressed by this proposal is dart-lang/sdk#30520, where we would use the following (which is assumed to occur in a function body):
Grammar
The grammar is adjusted as follows:
Static Analysis
A local import can occur in the body of a function. It is a compile-time error unless each name in the identifier list of a local import denotes a library prefix, a class, an enumeration, or an object.
Consider an identifier reference
id
which occurs in the body of a function F. Assume thatid
is not declared in an enclosing scope.If F or one of the enclosing functions of F is an instance method, and local imports of the identifiers
id1 .. idk
exist in one of the enclosing lexical scopes before the location of said identifier referenceid
, then consider the termsthis.id
,id1.id
, ..,idk.id
. It is a compile-time error if zero or more than one of these terms resolves statically to a member of an interface of an object, a static class member, an enumeration value, or a declaration exported by a library; otherwiseid
is treated as the single term which is not an error (that is,this.id
, oridj.id
for somej
).Otherwise (if F and each function that encloses F directly or indirectly is not an instance method) then the same treatment is given to
id
, except that only the termsid1.id
, ..,idk.id
are considered.Dynamic Semantics
The dynamic semantics of this feature is fully determined by the syntactic transformation which is described in the previous section.
Discussion
The ability to import the name space of an object is a generalization of the treatment of
this
in the bodies of instance methods and constructors, and this may be used to reduce the binding ofthis
in an anonymous method (#260) to syntactic sugar for a local import, provided that we also have the ability to bindthis
. That allows us to split the binding ofthis
and the import ofthis
(known as implicit member access in #260) into two independent features.The ability to import library prefixes and static class name spaces could be used to make certain function bodies less busy without polluting the global name space; but it would also be possible to allow imports to occur, say, at the beginning of a class.
The ability to have local imports anywhere in a function body could also be restricted (e.g., such that they can only occur before all statements and local declarations). This would make it easier to always spot all local imports at a glance, but could then make it harder to see the relevant import when it is actually used, and it would make the name space more busy before that point.
Note that this mechanism could support chaining. For instance,
import p, C;
could makex
desugar top.C.x
:x
could desugar toC.x
becausex
is a static member of the classC
, andC
would desugar top.C
because the library with prefixp
declares the classC
. However, the current wording intentionally does not support this scenario. The technical reason is that the names in the<identifierList>
of a local import must resolve to a library prefix, class, enumeration, or object without any desugaring steps, the desugaring is only applicable to expressions.We could allow local imports to contain a
<qualified>
term likep.C
, rather than just identifiers, but this would be a non-breaking change that we could perform at any time if it turns out to be needed.The text was updated successfully, but these errors were encountered: