Comments (10)
The biggest issue is noSuchMethod and its Invocation.memberName, which can be a private member's name, and you may want to compare against it, even on platforms that don't have dart:mirrors. For that, we do need something (and not just strings.)
@lrhn I don't understand. I thought Symbol literals are just syntax sugar for invoking a constant Symbol constructor? That is, for all x, where
#x
is supported,identical(#x, const Symbol('x'))
is always true?Are there any limitations that prevent the constant Symbol constructor from accepting string literals that represent private member names? Wouldn't invoking the constant Symbol constructor be a complete replacement for symbol literals?
The issue is that since Dart uses library-scoped privacy, #_foo
in one library is not the same as #_foo
in another library. Whereas Symbol('_foo')
, being a const expression, ought to produce the same result regardless of what library you invoke it from.
from language.
If it was only mirrors which needed symbols, I'd be happy to remove the syntax entirely, and have you to do
MirrorSystem.getSymbol("_foo", libraryMirror)
to get a private name's symbol.
Function.apply
only needs public symbols, since named parameters cannot have private names. It's also so rare that usingconst Symbol("a")
is probably fine. (I doubt most people even remember thatFunction.apply
exists.)The biggest issue is
noSuchMethod
and itsInvocation.memberName
, which can be a private member's name, and you may want to compare against it, even on platforms that don't havedart:mirrors
. For that, we do need something (and not just strings.)
FWIW, even if we remove #
syntax, it's still possible to construct a Symbol
that refers to a private name (but you can't do it in a const context):
class SymbolFactory {
const SymbolFactory();
Symbol noSuchMethod(Invocation invocation) {
if (invocation.isGetter) return invocation.memberName;
return super.noSuchMethod(invocation);
}
}
const dynamic symbolFactory = SymbolFactory();
final symbol_foo = symbolFactory._foo;
main() {
print(#_foo == symbol_foo); // prints `true`.
}
from language.
FWIW, even if we remove
#
syntax, it's still possible to construct a Symbol that refers to a private name
True, but if we remove invoking noSuchMethod
for private names, even if accessible, the need and ability would both go away.
from language.
I thought the ambiguity was handled by precedence, with continuing the symbol literal having higher precedence than property accesses. If so, the problem is handled, and if you really want to do #foo.toString()
, you'll have to do (#foo).toString()
. And that's fine, because ... like, why? And like, don't!
There are other uses for #foo.bar.baz
in dart:mirrors
. A "qualified name" like #library_name.ClassName.memberName
can be used to denote a declaration.
That has always been questionable, because that's not a source name, and symbols are supposed to represent source names.
And it doesn't work with private names, because #library_name._private_name
doesn't name-mangle to a private name in that library. (I have no idea what the qualifiedName
getter returns for a private declaration.)
Qualified names in dart:mirrors
should probably be avoided in general, and forcing them to use const Symbol(...)
instead shouldn't change anything.
Also, the specification is already that #_foo.bar
and #foo._bar
are unaffected by library privact, so using the Symbol
constructor should be sufficient.
The only real use for symbols in the language is for noSuchMethod
and Function.apply
.
So, LGTM.
from language.
I'd recommend that we eliminate the support for these symbol literals containing a dot separated sequence of identifiers from the language, thus eliminating this syntactic ambiguity entirely.
Agreed. Kill it with fire.
from language.
I would like to throw an alternative approach for removing this ambiguity into the room, which is to replace (and free up!) the #...
syntax with some syntactically sane (#<<<<#<<<#<<<<#<<
can be made a valid expression with extensions) symbol literal syntax like, for example, #"..."
.
There are other minor inconsistencies between the spec and the implementation.
(see dart-lang/sdk#50776 for more info, where @eernstg provides some more context)
For example:
var x = #[ ] =;
=== pkg:analyzer (https://pub.dev/packages/analyzer) ===
Parsing failed with some errors:
Scan errors: 0
Parse errors: 3
file(9..9): Expected an identifier.
file(11..11): Expected an identifier.
file(14..14): Expected an identifier.
<CompilationUnitImpl> [0-15]
┗━ <TopLevelVariableDeclarationImpl> [0-15]
┣━ <VariableDeclarationListImpl> [0-14]
┃ ┣━ 'var' [0-3]
┃ ┗━ <VariableDeclarationImpl> [4-14]
┃ ┣━ 'x' [4-5]
┃ ┣━ '=' [6-7]
┃ ┗━ <AssignmentExpressionImpl> [8-14]
┃ ┣━ <IndexExpressionImpl> [8-12]
┃ ┃ ┣━ <SymbolLiteralImpl> [8-9]
┃ ┃ ┃ ┣━ '#' [8-9]
┃ ┃ ┃ ┗━ '' [9-9]
┃ ┃ ┣━ '[' [9-10]
┃ ┃ ┣━ <SimpleIdentifierImpl> [11-11]
┃ ┃ ┃ ┗━ '' [11-11]
┃ ┃ ┗━ ']' [11-12]
┃ ┣━ '=' [13-14]
┃ ┗━ <SimpleIdentifierImpl> [14-14]
┃ ┗━ '' [14-14]
┗━ ';' [14-15]
--------------------------------------------------------------------------------
=== DSP (https://github.com/dart-lang/sdk/blob/master/tools/spec_parser/dart_spec_parser/Dart.g4) dspVersion v0.41 ===
Parsing succeeded with no errors.
Errors of type 1: []
Errors of type 2: []
<startSymbol>
┗━ <libraryDefinition>
┣━ <metadata>
┣━ <topLevelDefinition>
┃ ┣━ <varOrType>
┃ ┃ ┗━ 'var'
┃ ┣━ <identifier>
┃ ┃ ┗━ 'x'
┃ ┣━ '='
┃ ┣━ <expression>
┃ ┃ ┗━ <conditionalExpression>
┃ ┃ ┗━ <ifNullExpression>
┃ ┃ ┗━ <logicalOrExpression>
┃ ┃ ┗━ <logicalAndExpression>
┃ ┃ ┗━ <equalityExpression>
┃ ┃ ┗━ <relationalExpression>
┃ ┃ ┗━ <bitwiseOrExpression>
┃ ┃ ┗━ <bitwiseXorExpression>
┃ ┃ ┗━ <bitwiseAndExpression>
┃ ┃ ┗━ <shiftExpression>
┃ ┃ ┗━ <additiveExpression>
┃ ┃ ┗━ <multiplicativeExpression>
┃ ┃ ┗━ <unaryExpression>
┃ ┃ ┗━ <postfixExpression>
┃ ┃ ┗━ <primary>
┃ ┃ ┗━ <literal>
┃ ┃ ┗━ <symbolLiteral>
┃ ┃ ┣━ '#'
┃ ┃ ┗━ <operator>
┃ ┃ ┣━ '['
┃ ┃ ┣━ ']'
┃ ┃ ┗━ '='
┃ ┗━ ';'
┗━ '<EOF>'
The implementation does not support whitespace there, but the spec appears to do. This is not a big deal, but what for? All of this is unnecessary complexity for, in my opinion, very little benefit.
Giving symbols a distinct literal that is properly terminated on the lexical level would be easier to parse, easier to specify and easier to understand all while supporting the old #id.id.id use cases as, e.g., #"id.id.id" and it could support interpolations to make it a complete replacement for the Symbol constructor (#"id.${my_id}.id"
).
Personally, I really dislike the # symbol for representing Symbols. C uses # to switch to what could be considered a "meta" level and I really like that idea. Dart could reserve #
for similar uses in the future and replace symbol literals with, for example, s"id"
or something like an instance of a tagged string (https://github.com/dart-lang/language/blob/main/working/tagged-strings/feature-specification.md).
from language.
Personally, I really dislike the # symbol for representing Symbols.
Agreed. I would remove symbol literals from the language entirely if it were up to me. They don't carry their weight.
from language.
If it was only mirrors which needed symbols, I'd be happy to remove the syntax entirely, and have you to do MirrorSystem.getSymbol("_foo", libraryMirror)
to get a private name's symbol.
Function.apply
only needs public symbols, since named parameters cannot have private names. It's also so rare that using const Symbol("a")
is probably fine. (I doubt most people even remember that Function.apply
exists.)
The biggest issue is noSuchMethod
and its Invocation.memberName
, which can be a private member's name, and you may want to compare against it, even on platforms that don't have dart:mirrors
. For that, we do need something (and not just strings.)
Or we could make it impossible to get noSuchMethod
invocations for private names, even when they are in scope. (We did that for private names that were not in scope, making an implicit implementations into "nSM-throwers" instead of "nSM-forwarders".) And we could also make structurally or type-wise invalid dynamic invocations throw, instead of invoking noSuchMethod
, so the only way to get a noSuchMethod
invocation is through an unimplemented public interface member's nSM-forwarder. Nothing dynamic, that's just errors. (Currently a class Foo {noSuchMethod(i) => ...; void foo(int x);}
will have (Foo() as dynamic).foo("A")
throw a type error, but (Foo() as dynamic).foo("A", 0)
invoke noSuchMethod
. Good times! Much consistent!)
If we did that, then no user code outside of dart:mirrors
would need private symbols, so we could remove the syntax and defer to const Symbol("foo")
and const Symbol("[]=")
for everything.
(And get #
free for other uses. I call dibs on code units literals, #"a"
== 0x61! Surely everybody needs that!)
One other, non-essential, use of symbols is as unique non-forgable sentinel values.
I use them, fx, as the keys for zone values. Any code in the same library can just write #_zoneKey
as a constant, without needing to create a new class to make a unique instance of.
Don't know if there is something we can do to help with that. The workaround is just enum _Token { token; }
and using _Token.token
as the unique, unforgable value.
from language.
The biggest issue is noSuchMethod and its Invocation.memberName, which can be a private member's name, and you may want to compare against it, even on platforms that don't have dart:mirrors. For that, we do need something (and not just strings.)
@lrhn I don't understand. I thought Symbol literals are just syntax sugar for invoking a constant Symbol constructor? That is, for all x, where #x
is supported, identical(#x, const Symbol('x'))
is always true?
Are there any limitations that prevent the constant Symbol constructor from accepting string literals that represent private member names? Wouldn't invoking the constant Symbol constructor be a complete replacement for symbol literals?
from language.
I don't think losing private name forwarding to noSuchMethod
would be too disruptive. It does look like the #_uniqueKey
pattern is used often enough that we'd want to have an automated fix if we remove that syntax.
from language.
Related Issues (20)
- Support debugging Dart projects with compilation errors HOT 3
- Pattern matching allows you to refer to its own scope HOT 3
- Proposal: add a context for RHS of equality operations. HOT 1
- matching default values in switch statements (with record patterns) HOT 4
- Failing analysis of macro test prevents other actions HOT 1
- [augmentation-libraries] Missing grammar rule for extension and enum augmentations? HOT 6
- Assignment expressions in `if`-statements don't correctly promote nullable variables HOT 4
- Update the spec parser to take stop and continuation tokens into account for `<typeArguments>` as a selector
- More Concise Syntax for Multiple Conditions HOT 9
- Spec and implementations disagree about context for return/yield in function expressions HOT 1
- Spec is ambiguous about class hierarchy of enums; leads to observable differences in front end and analyzer behavior. HOT 2
- Resolve to non-`null` version of extension when available and criteria are met HOT 5
- Performance cost of .every() vs loops? HOT 4
- Assignability of generic type `Never` HOT 6
- Support trailing commas in `<catchPart>` HOT 5
- Allow pattern matching on error handling
- Make sealed type hierarchies more pleasurable to use HOT 3
- Require augmentation libraries to link to their main library, not the parent HOT 11
- Commits in the language repository fail because of analyzer failure in macro_proposal HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from language.