Modular At-Names

# Kevin Smith (13 years ago)

ES6 is shaping up quite nicely. The last big area which I feel is still quite foggy is syntactic support for symbolic property names, which Allen has addressed with his at-names proposal. Perhaps syntax won't make it into ES6 regardless of the work we do, but I think the symbol "story" isn't finished until we've got something.

With that in mind, I've created a sketch of an alternative at-name syntax story. I've tried to include enough detail to show that it might work, but not so much that I never finish the sketch : )

The basic idea is that modules implicitly define symbol namespaces. Symbols can be imported from other modules, or from a "world" symbol map which contains interned symbols for the entire execution environment.

Read on: gist.github.com/3868131

The actual design sketch follows some initial examples.

# Axel Rauschmayer (13 years ago)

Given that you need to import symbols, I prefer explicit declarations (which will also be used for exporting).

# Axel Rauschmayer (13 years ago)

Given that you need to import symbols, I prefer explicit declarations (which will also be used for exporting).

# Kevin Smith (13 years ago)

Given that you need to import symbols, I prefer explicit declarations (which will also be used for exporting).

Thanks for your feedback!

The problem with explicit declarations is that it's going to be burdensome, for no apparent gain. Consider: I have a real-world class with 37 "internal" methods which are prefixed with an underscore. I'm not kidding. Explicit declaration means that I have to maintain a list of 37 at-name declarations, separate from the class definition itself, which contributes no real information that isn't available elsewhere in the module code.

# Kevin Smith (13 years ago)

Also, I should mention a couple of things:

  1. This design essentially paves the cowpath trod with underscore-prefixed property names, adding the desired property of conflict elimination.

  2. If "System" is a built-in module instance, and the @iterator symbol is defined internally within that module as:

    sym @iterator = "org.ecma-whatever.es.6.iterator"; // Or perhaps more likely a uuid...

Then we have exactly the desired syntax that we're looking for:

import @iterator from System; // no funky "@belch" URLs!!

class C {

  @iterator() { ... }
}

And it works cross-frame, cross-loader-context and all.

# David Herman (13 years ago)

On Oct 12, 2012, at 9:38 AM, Kevin Smith <khs4473 at gmail.com> wrote:

The problem with explicit declarations is that it's going to be burdensome, for no apparent gain. Consider: I have a real-world class with 37 "internal" methods which are prefixed with an underscore. I'm not kidding. Explicit declaration means that I have to maintain a list of 37 at-name declarations, separate from the class definition itself, which contributes no real information that isn't available elsewhere in the module code.

This is a strong point. My first instinct was similar to Axel's, but you're right that classes in particular tend to come with a lot of names. I still need to mull this. Thanks for this proposal.

# David Herman (13 years ago)

On Oct 12, 2012, at 12:45 PM, Kevin Smith <khs4473 at gmail.com> wrote:

Also, I should mention a couple of things:

  1. This design essentially paves the cowpath trod with underscore-prefixed property names, adding the desired property of conflict elimination.

Yes, although I don't take the "private, who needs it?" attitude as easily as you do. Not saying it's wrong, but I'm not 100% convinced yet. Anyway, still mulling, as I say.

  1. If "System" is a built-in module instance, and the @iterator symbol is defined internally within that module as:

    sym @iterator = "org.ecma-whatever.es.6.iterator"; // Or perhaps more likely a uuid...

Then we have exactly the desired syntax that we're looking for:

import @iterator from System; // no funky "@belch" URLs!!

class C {

  @iterator() { ... }
}

Doesn't this have the same problem you were arguing against in your previous message, that if you want to import 37 names, you have to name them all individually?

# David Herman (13 years ago)

Yeah, just wasn't sure what I thought yet. Still not sure, but I've replied.

# David Herman (13 years ago)

WHOA. That appears to be a horrifying Apple mail bug. Not sure how my reply to a private message ended up going to es-discuss, sorry.

# Kevin Smith (13 years ago)

Yes, although I don't take the "private, who needs it?" attitude as easily as you do. Not saying it's wrong, but I'm not 100% convinced yet.

In a way I'm posing a challenge - I think it will be an interesting debate to have.

Doesn't this have the same problem you were arguing against in your

previous message, that if you want to import 37 names, you have to name them all individually?

Sure, if we were inclined to import 37 names, but I don't think that will be the pattern. Look at current usage of "underscored" property names. Their usage is mostly confined to a single site (usually a class). Only occasionally will you find an underscored property name whose semantic origin is non-local (usually in class hierarchies).

# Yehuda Katz (13 years ago)

During the last TC39, I converted a large Ember class (Ember.View) to ES6-style classes with private names.

I ended up needing a huge number of explicit declarations, which was very annoying to me. At the time, I proposed making classes an implicit symbol namespace. That didn't work because it would prevent using private names in imperative code. Because the current class definition forces the use of imperative code for many constructs (including static methods), this was fatal.

This approach is interesting, because it implies a 1:1 mapping between a "privacy context" and modules. At first glance, this seems plausible, but I don't have enough (any?) experience using ES6 modules in a real-world context to know whether this would force the creation of many extra modules simply to force a new namespace.

Is there any precedent we can lean on here?

Yehuda Katz (ph) 718.877.1325

# Allen Wirfs-Brock (13 years ago)

The gist of Kevin's proposal seems to be two key points:

  1. allow implicit declaration of (non-private) symbols rather than requiring explicit declaration
  2. use module scope rather than lexical scope for symbols.

Registration/lookup of symbol values in a global registry using string keys seems like a separable concept

Both of the key points seem to be classic design alternatives for which the pros and cons have been widely argued. The generally accepted consensus seems to be that explicit declarations are better (less error prone) than implicit and that lexical scoping is better than (semi-) global scoping. It's not clear to me that we really have a situation that is strongly contrary to the classic resolution of these arguments.

I agree that at-names need to be exportable and importable, but I don't see why we need anything more than support for things like:

import @iterator from '@System";

or

import {@save, @load} from Persistable;

For the case of importing "37" at-names, I would expect that * imports would take care of it:

import * from ModuleDefining37NameInterface;

For exports I would think you need to be explicit regardless:

export symbol @sym1, @ym2, @sym3, ... , @sym37;

# Kevin Smith (13 years ago)

Just wanted to comment that, indeed, exported at-names should be explicit. I'll try to update the gist when time permits.

# Kevin Smith (13 years ago)

For the case of importing "37" at-names, I would expect that * imports would take care of it:

import * from ModuleDefining37NameInterface;

Oh, come on!

The problem isn't importing the names. The problem lies in having to maintain a nightmarishly long set of declarations which are completely internal to the module, not exported and not imported.

# Yehuda Katz (13 years ago)

Yehuda Katz (ph) 718.877.1325

On Mon, Oct 15, 2012 at 4:19 PM, Kevin Smith <khs4473 at gmail.com> wrote:

For the case of importing "37" at-names, I would expect that * imports

would take care of it:

import * from ModuleDefining37NameInterface;

Oh, come on!

The problem isn't importing the names. The problem lies in having to maintain a nightmarishly long set of declarations which are completely internal to the module, not exported and not imported.

This is essentially the problem I had when I tried to port existing code. It was especially annoying because it was easy to forget to create and maintain the declarations.

The more I think about it, the more I think that scoping symbols to modules would work well. Does that mean symbols are simply disallowed outside of modules?

# Brendan Eich (13 years ago)

Have to say I agree with Kevin and Yehuda here, on implicit declaration (not export or import) being better. Not saying I'm sold on the whole idea, but I feel the pain.

The module-wide scope is pretty much Dart. Anyone using Dart much want to comment on how that's worked out?

# Axel Rauschmayer (13 years ago)

One thing to consider: I would expect IDEs to help with this. For example, Eclipse does a pretty good job of letting one forget that one has to import things before one can use them.

# Brendan Eich (13 years ago)

Axel Rauschmayer wrote:

One thing to consider: I would expect IDEs to help with this. For example, Eclipse does a pretty good job of letting one forget that one has to import things before one can use them.

Maybe, but forget IDEs. I think Kevin's point about private @foo, @bar, ...; being required in a class using those 25 private members will get old, fast.

The module vs. let scope is also interesting. Allen said the literature favored the latter but that wasn't clear from my nowhere-near-comprehensive reading.

The answers to the design questions here should not depend on IDEs.

# Allen Wirfs-Brock (13 years ago)

On Oct 15, 2012, at 6:02 PM, Brendan Eich wrote:

Axel Rauschmayer wrote:

One thing to consider: I would expect IDEs to help with this. For example, Eclipse does a pretty good job of letting one forget that one has to import things before one can use them.

Maybe, but forget IDEs. I think Kevin's point about private @foo, @bar, ...; being required in a class using those 25 private members will get old, fast.

But if you want private symbols you are going to have to say something anyway to distinguish them from regular "public" unique name symbols.

At the last TC39 meeting, I believe we agreed that I should expand the private name syntax proposal to include allowing private as a prefix to concise methods. So you could say:

class Foo { private @x() {} private @y() {} }

as an alternative to:

class Foo { private @x, @y; @x() {} @y() {} }

The prefix is actually more total characters in this case, but if you had 25 of them it would probably be easier to manage.

If you are using private symbols for per instance state you need to be explicit about it so it can be referenced byboth method bodies and the constructor:

class Foo { private @state; get state() {return @state}; set state((value) {@state = value} constructor (initialState) { this. at state = initialState; } }

If at the module level, you are defining a set of "public" symbols that will be exported so they can be used as names in public interfaces you need to be explicit about the export:

module M { export symbol @a, @b, @c, @d export class Foo { @a() {}; @b() {} ... } }

So, it seems like the only time when you might get away without explicit symbols declarations would be for module local "public" symbols that are shared among multiple classes defined within the module.

The module vs. let scope is also interesting. Allen said the literature favored the latter but that wasn't clear from my nowhere-near-comprehensive reading.

Presumably that is a large part of our motivation for providing lexically scoped let/const/function/class rather than the semi-global function scoping.

I believe the main arguments against implicit declarations are:

  1. they catch misspelling inconsistencies (but not consistent misspellings)
  2. they prevent unintended share via coincidental common name selection
# Yehuda Katz (13 years ago)

Yehuda Katz (ph) 718.877.1325

On Mon, Oct 15, 2012 at 9:46 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Oct 15, 2012, at 6:02 PM, Brendan Eich wrote:

Axel Rauschmayer wrote:

One thing to consider: I would expect IDEs to help with this. For example, Eclipse does a pretty good job of letting one forget that one has to import things before one can use them.

Maybe, but forget IDEs. I think Kevin's point about private @foo, @bar, ...; being required in a class using those 25 private members will get old, fast.

But if you want private symbols you are going to have to say something anyway to distinguish them from regular "public" unique name symbols.

At the last TC39 meeting, I believe we agreed that I should expand the private name syntax proposal to include allowing private as a prefix to concise methods. So you could say:

class Foo { private @x() {} private @y() {} }

as an alternative to:

class Foo { private @x, @y; @x() {} @y() {} }

The prefix is actually more total characters in this case, but if you had 25 of them it would probably be easier to manage.

In my experiment transforming existing code, that did in fact help, but there were still a large chunk of private name declarations at the top of my class.

If you are using private symbols for per instance state you need to be explicit about it so it can be referenced byboth method bodies and the constructor:

class Foo { private @state; get state() {return @state}; set state((value) {@state = value} constructor (initialState) { this. at state = initialState; } }

Right.

If at the module level, you are defining a set of "public" symbols that will be exported so they can be used as names in public interfaces you need to be explicit about the export:

module M { export symbol @a, @b, @c, @d export class Foo { @a() {}; @b() {} ... } }

So, it seems like the only time when you might get away without explicit symbols declarations would be for module local "public" symbols that are shared among multiple classes defined within the module.

Or, more commonly, symbols that are used in a single class that happens to be inside a module. Although I can't be sure about this, I expect that a large number of modules will exist to encapsulate a single class. This is based on my experience breaking up large JavaScript projects by modules today, where I rarely have multiple "classes" in a single module.

The module vs. let scope is also interesting. Allen said the literature favored the latter but that wasn't clear from my nowhere-near-comprehensive reading.

Presumably that is a large part of our motivation for providing lexically scoped let/const/function/class rather than the semi-global function scoping.

I believe the main arguments against implicit declarations are:

  1. they catch misspelling inconsistencies (but not consistent misspellings)

This is definitely a valid concern. Ruby has a similar problem via instance variable names (which coincidentally are also @foo), and tries to address this via a warning if an uninitialized instance variable is used. It's not a particular popular warning in Ruby, fwiw.

  1. they prevent unintended share via coincidental common name selection

Again, because I expect that most modules will not contain many classes, I don't expect this to be a major issue. If the classes in a module are small enough to justify multiple classes, it will be obvious that the same name is reused.

That said, my original comments to this proposal addressed exactly this issue: we might end up seeing people defensively overusing modules to wrap classes in order to guarantee that private names don't leak out. Upon further reflection, I don't think this is such a major issue because good practice will lead to few classes per module anyway.

# Domenic Denicola (13 years ago)

From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Yehuda Katz Sent: Tuesday, October 16, 2012 09:13

Or, more commonly, symbols that are used in a single class that happens to be inside a module. Although I can't be sure about this, I expect that a large number of modules will exist to encapsulate a single class. This is based on my experience breaking up large JavaScript projects by modules today, where I rarely have multiple "classes" in a single module.

I just want to second how common this is in my experience, both in Node.js and front-end projects. By far the most common module in projects I’ve seen looks like

module.exports = function Class() { };

(which there isn't even a syntax for in ES6, notably.) For example out of the 150 modules in our current project at work, 4 of them do not follow this pattern.

# Kevin Smith (13 years ago)
  1. The one-class-per-file pattern is near universal. When there is more than one class, they tend to be in minor supporting roles (like exception-type subclasses or simple data structures).

  2. Adding private, protected, etc. declarations to classes adds a good deal of baggage to the syntax that many developers will balk at. I'm not sure how to argue this yet, but it just doesn't have the "spirit" of javascript.

  3. "Implicit declaration" is the wrong way to think about modular at-names. At-names, in this design, are simply namespaced identifiers. Since modules already define an implicit namespace, it's reasonable and convenient to hang these special identifiers off of that namespace.

# Yehuda Katz (13 years ago)

Yehuda Katz (ph) 718.877.1325

On Tue, Oct 16, 2012 at 9:45 AM, Kevin Smith <khs4473 at gmail.com> wrote:

  1. The one-class-per-file pattern is near universal. When there is more than one class, they tend to be in minor supporting roles (like exception-type subclasses or simple data structures).

  2. Adding private, protected, etc. declarations to classes adds a good deal of baggage to the syntax that many developers will balk at. I'm not sure how to argue this yet, but it just doesn't have the "spirit" of javascript.

More importantly, it's too easy to mess up.

  1. "Implicit declaration" is the wrong way to think about modular at-names. At-names, in this design, are simply namespaced identifiers. Since modules already define an implicit namespace, it's reasonable and convenient to hang these special identifiers off of that namespace.

I think this makes sense. What do you think about the typo problem? Not a major issue?

# Kevin Smith (13 years ago)

What do you think about the typo problem? Not a major issue?

It's javascript, so I guess I'm used to getting pinched every now and then by a little property misspelling. It's never caused me to miss C#/C++/Java/etc. And it all washes out with proper testing.

It seems orthogonal to me anyway: if we really want to address member name typos, then we want some kind of type system...

(...well, I personally don't want a type system, but you know...)

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

The module vs. let scope is also interesting. Allen said the literature favored the latter but that wasn't clear from my nowhere-near-comprehensive reading.

Presumably that is a large part of our motivation for providing lexically scoped let/const/function/class rather than the semi-global function scoping.

True for bindings but not clear for private/friend visibility qualifiers. Other languages do not all block-scope those, more the reverse: class or package/library scope.

I believe the main arguments against implicit declarations are: 1) they catch misspelling inconsistencies (but not consistent misspellings)

Good parenthetical point (and one near and dear to my heart, probably yours too ;-).

2) they prevent unintended share via coincidental common name selection 

You mean the allow unintended sharing?

Yes, private prefixes and declarations are more powerful. Main questions I see Kevin raising are usability and overkill. Say we reject "overkill" for good reason. We may still have usability woes. 'private' is seven long letters, eight with the space after.

# Herby Vojčík (13 years ago)

Allen Wirfs-Brock wrote:

On Oct 15, 2012, at 6:02 PM, Brendan Eich wrote:

Axel Rauschmayer wrote:

One thing to consider: I would expect IDEs to help with this. For example, Eclipse does a pretty good job of letting one forget that one has to import things before one can use them. Maybe, but forget IDEs. I think Kevin's point about private @foo, @bar, ...; being required in a class using those 25 private members will get old, fast.

But if you want private symbols you are going to have to say something anyway to distinguish them from regular "public" unique name symbols.

At the last TC39 meeting, I believe we agreed that I should expand the private name syntax proposal to include allowing private as a prefix to concise methods. So you could say:

class Foo { private @x() {} private @y() {} }

as an alternative to:

class Foo { private @x, @y; @x() {} @y() {} }

I am not very fond of this syntax, I'd better not to have it there, nevertheless.

But if I embrace it, I have a question: what about non-private symbols? Do they need to be explicitly declared elsewhere, but privates can be declared online? IOW, shouldn't we then allow also

class Foo { private @x() {} public @y() {} }

And if yes doesn't this mean it is better to use public instead of symbol in standalone non-private symbol declarations, as well, for the consistency?

# Brendan Eich (13 years ago)

Herby Vojčík wrote:

And if yes doesn't this mean it is better to use public instead of symbol in standalone non-private symbol declarations, as well, for the consistency?

Syntax is being debated. Allen used 'symbol' but 'public' is another candidate (and already reserved).

# Allen Wirfs-Brock (13 years ago)

On Oct 16, 2012, at 9:11 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

The module vs. let scope is also interesting. Allen said the literature favored the latter but that wasn't clear from my nowhere-near-comprehensive reading.

Presumably that is a large part of our motivation for providing lexically scoped let/const/function/class rather than the semi-global function scoping.

True for bindings but not clear for private/friend visibility qualifiers. Other languages do not all block-scope those, more the reverse: class or package/library scope.

Yes, but what are we talking about here. ES6 symbols are a kind of value, not a visibility qualifier. At-names, are a name binding for those values. Neither of these are visibility qualifiers, that is just a use case for them (although probably the primary use case). We can't just look at what other languages do for visibility qualifiers, we also have to work in the context at how name binding is handled in ES.

The symbol path is a different approach to controlling member access than used in C++/Java etc. Symbols are much closer in-concept to Ruby instance variables, except that in Ruby the bindings are scoped to a class (actually a class hierarchy, in that sense they have "protected" visibility).

I believe the main arguments against implicit declarations are:

  1. they catch misspelling inconsistencies (but not consistent misspellings)

Good parenthetical point (and one near and dear to my heart, probably yours too ;-).

Indeed!

  1. they prevent unintended share via coincidental common name selection

You mean the allow unintended sharing?

yes

Yes, private prefixes and declarations are more powerful. Main questions I see Kevin raising are usability and overkill. Say we reject "overkill" for good reason. We may still have usability woes. 'private' is seven long letters, eight with the space after.

"priv"?? "sym"?? Plus as an OO developer, "protected" is what I really want...

We also have the issue that we have orthogonal differences (reflection) between "public" and private symbols and as long as we have them, there needs be a way to designate which is intended.

The root question might be whether the symbol approach truly provides a usable solution for the encapsulation use case.

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

On Oct 16, 2012, at 9:11 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

The module vs. let scope is also interesting. Allen said the literature favored the latter but that wasn't clear from my nowhere-near-comprehensive reading. Presumably that is a large part of our motivation for providing lexically scoped let/const/function/class rather than the semi-global function scoping. True for bindings but not clear for private/friend visibility qualifiers. Other languages do not all block-scope those, more the reverse: class or package/library scope.

Yes, but what are we talking about here.

If that's a question, see Kevin's head post. This is a thread about an alternative. It's not necessary to rehash your at-name proposal, which is clear enough. What would help IMHO is comparing it to Kevin's or other similar such things (Dart's, e.g.) based on use-cases and actual user experience.

"priv"?? "sym"?? Plus as an OO developer, "protected" is what I really want...

Bletch, barf, and too long :-P.

We also have the issue that we have orthogonal differences (reflection) between "public" and private symbols and as long as we have them, there needs be a way to designate which is intended. The root question might be whether the symbol approach truly provides a usable solution for the encapsulation use case. Allen

Agreed, this thread proposed an alternative that actually threw out privacy in order to avoid collisions and match today's JS patterns.

# Brendan Eich (13 years ago)

Brendan Eich wrote:

Agreed, this thread proposed an alternative that actually threw out privacy in order to avoid collisions and match today's JS patterns.

I should have written "... threw out privacy in order to simplify and focus only on avoiding collisions and matching ...."

# Yehuda Katz (13 years ago)

Yehuda Katz (ph) 718.877.1325

On Tue, Oct 16, 2012 at 11:48 AM, Kevin Smith <khs4473 at gmail.com> wrote:

What do you think about the typo problem? Not a major issue?

It's javascript, so I guess I'm used to getting pinched every now and then by a little property misspelling. It's never caused me to miss C#/C++/Java/etc. And it all washes out with proper testing.

It seems orthogonal to me anyway: if we really want to address member name typos, then we want some kind of type system...

(...well, I personally don't want a type system, but you know...)

I buy this.