Coder Social home page Coder Social logo

Comments (10)

stereotype441 avatar stereotype441 commented on June 16, 2024 2

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.

stereotype441 avatar stereotype441 commented on June 16, 2024 1

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.)

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.

lrhn avatar lrhn commented on June 16, 2024 1

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.

lrhn avatar lrhn commented on June 16, 2024

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.

munificent avatar munificent commented on June 16, 2024

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.

modulovalue avatar modulovalue commented on June 16, 2024

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.

munificent avatar munificent commented on June 16, 2024

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.

lrhn avatar lrhn commented on June 16, 2024

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.

modulovalue avatar modulovalue commented on June 16, 2024

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.

natebosch avatar natebosch commented on June 16, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.