public
Last active — forked from jashkenas/minimalist-classes.js

less minimalism, richer leather

  • Download Gist
minimalist-classes.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
// A response to jashkenas's fine proposal for minimalist JavaScript classes.
 
// Harmony always stipulated classes as sugar, so indeed we are keeping current
// JavaScript prototype semantics, and classes would only add a syntactic form
// that can desugar to ES5. This is mostly the same assumption that Jeremy
// chose, but I've stipulated ES5 and used a few accepted ES.next extensions.
 
// Where I part company is on reusing the object literal. It is not the syntax
// most classy programmers expect, coming from other languages. It has annoying
// and alien overhead, namely colons and commas. For JS community members who
// don't want classes, either proposal is "bad", so let's focus on the class
// fans who will use this feature.
//
// Therefore I propose giving classes their own new kind of body syntax, which
// is neither an object literal nor a function body. The main advantage is that
// syntax for class elements can be tuned to usability, even as it desugars to
// kernel semantics drawn from ES5 extended by both the super and private names
// proposals.
 
// Not shown below is the super proposal at
//
// http://wiki.ecmascript.org/doku.php?id=harmony:object_initialiser_super
//
// which composes as expected when 'super' is used in methods defined in a
// class instead of in an object literal.
//
// In contrast, you will see hints of the private name objects proposal at
//
// http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects
//
// Also visible throughout: the method definition shorthand syntax proposed at
//
// http://wiki.ecmascript.org/doku.php?id=harmony:object_literals#object_literal_property_shorthands
//
// For programmers who want classes as sugar for constructors and prototype, I
// hope this proposal wins.
//
// To be perfectly honest, I personally would be happy with Allen Wirfs-Brock's
// "Exemplars" approach and no classes. But for programmers coming from classy
// languages, I believe "minimalist classes" will tend to be too minimal to be
// usable out of the box. They'll want more.
//
// Brendan Eich, 1-Nov-2011
 
// First, basic usage from a real-world library (Three.js)
 
class Color {
 
// Commas are a curse in class syntax, not a blessing as in current object
// literal syntax. But with method definitions as proposed for ES6 object
// literals, commas could be optional. Allen proposed this at the last TC39
// meeting.
//
// Problems if commas are optional after data properties, but after methods
// should be ok.
 
constructor(hex) {
...
}
 
// DUBIOUS: prototype data properties are a footgun, Alex Russell points to
// hazards involving mutable objects. I observe that non-writable prototype
// data properties are unheard of, likely because you can't shadow them by
// assignment.
//
// But if we must have them, we could use object literal syntax for writable
// data properties as follows:
//
r: 1, g: 1, b: 1,
 
copy(color) {
...
}
 
setRGB(r, g, b) {
...
}
 
setHSV(h, s, v) {
...
}
 
}
 
 
// To create a class with its prototype chain set correctly:
 
class Fox extends Animal {
...
 
// The special syntax for defining class-level properties and methods uses a
// prefix keyword. The 'static' keyword is reserved and somewhat hated (or at
// least considered a misnomer), but I went with it instead of 'class' to
// avoid confusion in overloading the c-word, and to be more future-friendly
// in case nested classes are wanted (right now I do not want them, since a
// class at class-body level would declare a prototype inner class and not a
// static inner class).
//
// Class-side properties inherit, just as constructor-side properties do with
// the <| proposal:
//
// http://wiki.ecmascript.org/doku.php?id=harmony:object_literals#individual_extension_summary
 
static const CONSTANT: 42,
 
static classMethod() {
...
}
}
 
console.log(Fox.CONSTANT);
 
Fox.classMethod();
 
 
// Classes are expression forms as well as declaration forms. As with function
// expressions, the name is optional in a class expression.
 
const frozenClass = Object.freeze(class {...});
 
// Named class expressions are supported too. The name is bound only in the
// scope of the class body, as for named function expressions.
 
animals.push(class Fox {});
 
// An anonymous class expression with superclass specification, after Jeremy's
// gist, but with an explicit and required body.
 
var subclass = function(parent) {
return class extends parent {
...
};
};
 
 
// Unlike Jeremy's proposal, classes cannot be built up programmatically by
// abutting an expression to a class head. That's too dynamic, it doesn't work
// with the 'super' proposal. So any synthesis of a class body must use eval
// or equivalent. At this time, I do not propose adding a Class constructor
// analogous to Function, but I believe it could be added without issue.
 
 
// As in Jeremy's gist, here's the Monster class from the harmony: proposal
// (http://wiki.ecmascript.org/doku.php?id=harmony:classes#the_proposal_in_a_nutshell)
// ... sans unnecessary 'public' keyword prefixing!
 
class Monster {
 
constructor(name, health) {
this.name = name;
this.health = health;
}
 
attack(target) {
log("The monster attacks " + target);
}
 
isAlive() {
return this.health > 0;
}
 
setHealth(value) {
if (value < 0) {
throw new Error("Health must be non-negative.");
}
this.health = value;
}
 
numAttacks: 0,
 
const attackMessage: "The monster hits you!"
 
}
 
// Now there's one more problem to address, which could be deferred, but which
// falls under "batteries included" to some (not all) of the cohorts of classy
// programmers using JS or coming to it fresh in the future. And that is the
// cost of this[kPrivateName] given const kPrivateName = Name.create(...) where
// module Name from "@name" has been declared.
//
// Instead I propose we support private kPrivateName, ...; as a special form
// only in class bodies (for now) that both creates a private name object and
// binds it to a lexical const binding that may be accessed on the right of @
// in methods defined in a class.
//
// For class-private instance variables, obj@foo is supported as well (with no
// LineTerminator to the left of @ in this case, or it's short for this@foo).
// See the new sameName method for an example of infix and prefix @ in action.
//
// David Flanagan suggests keeping the unary-prefix @foo shorthand, but making
// the long-hand obj.@foo. Breaks E4X but no matter -- but the win is that the
// [no LineTerminator here] restriction on the left of unary-prefix @ is not
// needed. Also the references don't grep like email addresses, which counts
// with David and me. So I've incorporated David's suggestion. See other.@name
// etc. below.
//
// There is no const instance variable declaration. Non-writable instance vars
// (properties to most people) are quite rare. Use ES5's Object.defineProperty
// if you must. This avoids ugly fights about read before constant instance var
// initialization too.
//
// OOP often requires factoring common subroutines into private methods, so the
// 'private' keyword should be supported as a method prefix, just as 'static'
// is. Should we allow 'static private' methods too? Same rationale applies if
// you have two static (public) methods with a large common subroutine.
 
class Monster {
 
private { name, health } // can group a prefix, same for const and static
// note comma optional after closing brace, as for
// method definitions
 
public flair, // you should have 37 pieces of public flair
 
constructor(name, health) {
@name = name;
@health = health;
@flair = 0;
}
 
sameName(other) {
return @name === other.@name;
}
 
private attackHelper(target) {
...
}
 
attack(target) {
log("The monster attacks " + target);
return @attackHelper(target);
}
 
isAlive() {
return @health > 0;
}
 
setHealth(value) {
if (value < 0) {
throw new Error("Health must be non-negative.");
}
@health = value;
}
 
numAttacks: 0,
 
const attackMessage: "The monster hits you!"
 
}
 
// As in the harmony:classes proposal, 'const class' freezes all of
// 1. the class binding, if named declaration rather than expression,
// 2. the class prototype induced by the const class,
// 3. the constructor method of the const class.
//
// 'const class' also seals instances constructed by the constructor, as if the
// last line of the constructor method were Object.seal(this), called using the
// original values of Object and Object.seal.
 
const class Frosty {
}

I like. Still minimal, no function keyword, privates and statics. +1

It's bikeshedding but I don't like that method definitions look like function calls.

Why not copy CoffeeScripts constructors completely?

constructor(@name, @health){}

+1 static, private, const
still prefer : over extends

The "prototype data properties" have me very confused. Are they "public"? Why var instead of let? They remind me of the ES5 closures-as-classes "private" pattern.

How do I declare private methods? I'd want these to desugar to functions on the prototype that are accessible only through their private names.

Personally, I'd be up for public and private prefixes to make this whole thing a lot clearer.

@DomenicDenicola well @BrendanEich uses the static modifier for some method definitions (are they actually bound though? I don't remember if this semantics were defined), and the private modifier below for variables. I'd assume the private modifier to be valid to any declaration.

About public, I think it's far better if that's not required — all members are public unless otherwise specified. That's what you'd probably want them to be, and not making public required everywhere makes the whole code a hell lot cleaner — we can leave the verbosity to Java and other archaic statically typed languages, and borrow loads from Haskell and such instead :3

I'm not keen on var either. Other than that, I find this proposal pretty okay. I wonder how it would handle trait composition in the classes though. I remember there was something about that implemented in some ES.next→JS compiler, but can't remember which for life.

(I need to remember markdown doesn't recognize `code')

I agree with @grayrest's concern.

Also, why do var/const/let statements in a class body require semicolons when method definitions do not? That strikes me as odd.

@etlovett eh? By ECMAScript grammar, as far as I can remember, a block statement doesn't take a semicolon. Such that:

function foo() { };

Is actually a function statement followed by an empty statement.

Also, note that semicolons are not explicitly required to appear in the code in JavaScript, due to ASI rules.

Syntax: I like it.

  • Still lightweight, well motivated at the beginning.
  • A nice and subtle update of the object literal syntax. Makes me wish we could use it for object literals, too.
  • Using var is confusing. It goes against the general rule “‘var’ is bad, ‘let’ is good”. I’d use let or leave it out:

    class Foo {
        r = 1;
        g = 1;
        b = 1;
        const bar = "abc";
        copy(color) {
            ...
        } // methods are not followed by a semicolon
        x = 5  // final semicolon is optional
    }
    

    I’d expect people to get the semicolons intuitively right.

Private names:

  • For reasons of symmetry (with property access), I would require a lhs for @.
  • Couldn’t foo@bar be just syntactic sugar for foo[bar] (and then be used anywhere)?

@killdream - Two reasons:
1. Visual consistency - it's weird to see some "things" ending in semicolons and some not. FWIW, I feel this way about function expressions vs. "normal" lines (maybe "expression statements" is the right term?) in ES 5.
2. Comparison to the desugared form - wouldn't isAlive() { ... } within a class declaration body desugar to Monster.prototype.isAlive = function () { ... };? And that desugared form does require a semicolon at the end.

@rauschma I guess you beat me to the `var' nitpicking :3

@etlovett What I mean is that the function declarations inside the class-body not ending with a semicolon is not a "class-thing", it's specified by the ECMAScript grammar for block statements ( http://es5.github.com/#x12.1 ).

And no, if you want to preserve the syntax/grammar requirements, they would need to be desugared into:

Monster.prototype.isAlive = isAlive;
function isAlive() {
   ...
} // no semicolon here, since this is a block.

The thing is, if you put a semicolon after a block statement, you're not terminating the block statement, you're creating a new and empty statement, such that {}; generates two tokens: BlockStmt and EmptyStmt.

@grayrest: I see your point if you parse the body as a statement-list (block kids). But then you must favor object literal body instead? If not, how to fix?

@mythz: I love that formal-param-name-inits-ivar-of-same-name shorthand. Dart copied it. We can too, I simply chose to leave it out at this point -- but I'd champion it. As for : over extends, we have the latter reserved and I think that's enough to tilt the balance.

@DomenicDenicola: yes, public by default and no need for that 'public' keyword. Did you expect private by default? Not in JS! 'private' prefixes on methods are possible, I left them out for now. I do not see the need for 'public' prefixes but if redundant, maybe.

@etlovett: see @killdream's followup.

@rauschma: 'let' is lexical, these are properties. Conflict! I went with 'var' for auld lang syne but perhaps we can drop it and allow what look like assignment expression statements? Yikes.

As for foo@bar short for foo[bar], could be. As Red Leader said, stay on target!

@etlovett again: No, desugaring is not to statements, rather to method: function (...) {...}, property initialisers in object literals -- but that doesn't cut any ice for usability, which requires sugar which is not exactly object literals.

/be

@etlovett: Actually method definitions bind non-enumerable properties, so there is no statement or expression (part) desugaring. Anyway semicolon placement in a desugaring has zero to do with the sugared syntax, which can win in many ways -- including by not requiring semicolons!

I think the tension pulls the other way: toward object literal syntax. But then we'd need steenkin' commas as separators, which lose. I repeat that class body syntax should be bespoke.

/be

The nice thing about the method definitions that look like function calls is that people who want classes or people coming from C/Java world would feel at home with that syntax style. I can certainly appreciate it and I do like not having to type function. This syntax feels more classy.

I would rather Class() be well defined but optional. If an implementer decides to implement it, they have a target, and should multiple implementers implement it, they have consistency.

@Havvy what is the point in standardising something in ES.next without having all the JavaScript implementations include it? Interoperability is key. It's one of those really lovely things you get with standardisation and vendor support. Now, if you'd prefer to use object literals, just go for them. No reason they need to be mutually exclusive. Classes and object literals solve different problems anyways — right now.

@killdream To say "If we do standardize it, here is how it will be, but we will wait to see if there are any use cases before mandating implementation by all."

@Havvy But an ECMAScript standard already defines a mandatory implementation for all features described in the standard, if an implementor decides to implement the language...

@BrendanEich Honestly, I think @jashkenas's proposal feels a lot more like JavaScript—and I'm speaking as one of your "class fans!" Yes, it may be a little alien to people (like me) who are used to other classy languages, but I don't think they'll have trouble adjusting; it's pretty clear what's going on. More importantly, it's not alien to JavaScript. IMHO, it's much more important that the language be self-consistent. What separates a beautiful language from the cruddy ones is that all the pieces of the beautiful one feel like part of the same whole.

Where your version doesn't work for me is with the properties. As you mention (but I think understate), it very much looks like the methods should close over them. That would be confusing not just to the person coming from ClassyLangX, but also to the seasoned JS vet! In addition, it's not at all clear what's allowed there: can any arbitrary static initialization happen in the class body? In my opinion, this "property problem" makes the whole pattern untenable.

The object literal is clear and familiar. (I also like that it feels kind of similar to quasi-literals.) Unlike @jashkenas, I think the private/static/const keywords would be a welcome addition:

class Fox extends Animal {
    static const CONSTANT: 42,
    static classMethod: (a, b, c) -> {
        ...
    }
}

Beautiful! And feels like JavaScript!

There are a few things alien to JS in @jashkenas's proposal -- dynamic class bodies and super are two examples. Dynamic super has no performant and consistent semantics in today's JS -- not desugarable via local transformations.

Your example mixes declarative prefix keywords with object initializer syntax and arrow syntax; neither prefix keywords nor arrows occur in @jashkenas's gist. We should not make a stew of three proposals. At most two :-P.

/be

But then you must favor object literal body instead? If not, how to fix?

I favor object literal body but if things wind up going this way, I'd appreciate a keyword (def/method/function/operator/whatever) in front. The short syntax looks good on these short samples but when you start getting a bunch of 20-30 line methods, code folding, and fuzzy find your way in with no context, I'd like a few more signposts.

I liked Jeremy's proposal in that it was familiar, seemed to be paving the cowpath, the name: pattern is an immediate tell that you're in a no-statement region, and the static/class keys are really not that common. I even use yui3 at work, which I believe is the only major lib that features statics regularly. I'm a long time js fan (Moz 0.9.7 Seamonkey XPFE / Sunbird) and Pythonista which means I share the general Python community's views on member privacy and remain afraid of private/frozen declarations reprising as ghosts of AS3. There are good reasons to have them in the language but making them convenient invites use.

@DomenicDenicola: yes, public by default and no need for that 'public' keyword. Did you expect private by default? Not in JS!

FWIW (maybe not much), is AS3 (ES4 i guess?), the default modifier is not "public", it's "internal". Since we're not really talking about any semantics for "internal" (no packages, etc), the closest cousin would probably be "protected", and if there's no "protected" then "private". But I think the pattern leans more toward "not-public" by default than to "public". I don't know all the reasons for "not-public-by-default" but it seems like a reasonable default to me, and I know many AS developers that are perfectly happy and comfortable with that behavior.

If we swung toward "private-by-default" and people were worried/upset about having to type too many "public" modifiers, maybe we could borrow (sorta) from c++ and have the modifier as a "block" (or switch), like this:

class Fox extends Animal {
:public
   a: 2,
   foobar(b,c) { ... } // still public
:private
   myvar:"really private" // now private
}

Or

class Fox extends Animal {
   public a: 2,
      foobar(b,c) { ... } // still public
   private myvar:"really private" // now private
}

neither prefix keywords nor arrows occur in @jashkenas's gist.

@BrendanEich I think the prefix keywords were anticipated in his comments. They're an obvious and organic extension. Though not strictly necessary, they would add some of the power of your proposal.

 We should not make a stew of three proposals. At most two :-P.

I agree! Only two ways to define a function (; The arrow wasn't part of the proposal; I just meant to show how the already existing (or already proposed) mechanisms for defining functions (the function keyword and the arrow) were sufficient. Your syntax adds a third way to define a function (method). IMO, it falls into the AS3/E4X trap of solving too specific a problem without considering the broader implications (complications) of the language. The object literal form, on the other hand, dovetails nicely with your arrow function proposal (unless I'm misunderstanding it, which is entirely possible): the two compliment each other instead of competing and muddying up the language.

There are a few things alien to JS in @jashkenas's proposal -- dynamic class bodies and super are two examples.

Yes, and those things could be cut IMO without too much impact. To my mind, the meat of his proposal is the syntax for declaring that Monster class. Doesn't get more idiomatic than that.

IMO removing unnecessary ceremony like function (in this case) isn't "introducing something new" rather it's "removing something that's not necessary" to begin with.

-- Antoine would agree.

@getify I think "public by default" is pretty much set in stone. Or it should be. People are looking at Python for a model, not AS3. And given that everything in JS is currently "public," it just seems more in keeping with the language.

@mythz But that's what the arrow function syntax is meant to do. The non-object-literal class proposal would be a second (context dependent) way to achieve the same goal. Much better to take the wide view of the problem and solve it once. I am all for* classes in JS, but we have to look at the language holistically.

*really really really all for

@getify: ES4 namespaces including internal died with foundation of Harmony. See https://mail.mozilla.org/pipermail/es-discuss/2008-August/003400.html.JS is "public by default".

@matthewwithanm: see @jashkenas quote at http://brendaneich.com/brendaneich_content/uploads/CapitolJS.011.png and read about grammar validation issues with arrows. Not looking good. Holistic approach good, as always.

If classes should build on object literal body plan, then method syntax is fine but comma separators are required, and private/const/static prefixes are a struggle (do they also work in object literals? If not why not? etc.). If OTOH class body syntax is sui generis it can borrow genes from several lineages. Methods, data declarations, prefixes on same.

One could argue "anything goes" or "all extrapolation from current forms" but hewing too close to one lineage makes a "strong attractor". Hybrid approach may hit sweet spot. I'm still not sure, so keep the comments coming.

/be

ES4 namespaces including internal died with foundation of Harmony. See https://mail.mozilla.org/pipermail/es-discuss/2008-August/003400.html. JS is "public by default".

I never suggested we should (or would) have internal or ES4 namespaces. I'm well aware that most of that stuff died with ES4.

However, neither that assertion nor your link really address the actual point in question, which is "public by default" vs. "private (or, rather, non-public) by default". You've declared now twice "JS is public by default", but I wasn't aware of where that had been officially "decided". Do you have a more specific link where the public/private default was decided/declared, and/or where the discussions of the pros/cons are? Or is this just your feeling based on the existing (prototypal) precedent in javascript?

I'd just like to know why, if we're inventing new class syntax for javascript, the discussion of public/private by default isn't something that can even be up for discussion?


I think "public by default" is pretty much set in stone. Or it should be. People are looking at Python for a model, not AS3.

While I could agree that there are things that are more attractive in the python world vs. the AS3 world, in terms of inspiration, to me that doesn't mean we necessarily have to take everything ver batim. There's plenty of arguments for why the classical OO world says that things should be private-by-default (which I'm sure inspired AS3/ES4's decision some), but I'm not seeing much (yet) other than personal preference as to why JS needs to follow Python despite those reasons?

Set aside AS3 for the moment. C++ has private-by-default too. And there are others with that precedent. So surely this is a topic with merits on either side, right?

And given that everything in JS is currently "public," it just seems more in keeping with the language.

By "everything", I assume you mean that all prototypal-inherited things are public. That certainly ignores the closure-encapsulation (private) pattern, which is quite the opposite (everything declared with var or let is in fact private-by-default, and you have to work to make things public). In my world, I work (and prefer) VASTLY more with the closure-encapsulation pattern than with the prototypal inheritance pattern, so obviously that informs my bias toward private-by-default.

Again, I see this as a discussion that has pros and cons on both sides, and should be able to tolerate some mild discussion/debate. Is this a sacred cow (like spaces/tabs) that I'm just totally unaware of? Why the aversion to private-by-default for a totally new (to javascript) construct?

@getify: public by default is what exists today for the prototypal pattern. The closure pattern gives private ivars as an option (not necessarily by default), but we're not sugaring the closure pattern, as noted. It's pretty sweet already, syntactically, but it costs more inevitably due to closure per method per instance. Public by default is in fact the de-facto standard.

/be

Updated per comments here and based on forks -- thanks, this gist thing works a lot better than es-discuss email threads! ;-)

/be

Did you intend for the private declaration to be ;-terminated while the prototype property declarations are ,-terminated, or is that a typo?

Dave

@BrendanEich It's very close, my only issue is that it appears you have statements at the top above the constructor but have object literal expressions at the bottom? Do these have different capabilities/behaviours? i.e. can you not have statements at the bottom as well?

class Monster {
    private name, health;
    ...

    numAttacks: 0,
    const attackMessage: "The monster hits you!"
}

Commas separating data member definitions in the class body while method definitions have no separator whatsoever is really confusing and seems like it would be weird to parse.

@killdream No, in A.prototype.foo = function () {};, the semicolon is terminating an assignment expression that happened to end with a block statement, not an empty statement after a block statement. But that's beside the point...

@killdream, @BrendanEich If consistency is a goal, I'd rather we drop the semicolons altogether within class bodies, rather than add them to methods. Assuming creating that grammar is reasonable, given all the ways expressions can contain newlines, my preference would be to drop all terminators entirely - both semicolons and commas. As you said, Brendan, why not create a clean custom syntax here, while we have the chance?

Another benefit of dropping semicolons is that then you reduce the risk of people confusing it with lexically-closed-over variables, especially if you go with a non var-based syntax. For example, if @ weren't going to be used for private properties, I'd suggest using it instead of var.

One other thought, using the following example:

  class Example {
    constructor() { ... }
    var a = [];  //or whatever alternate syntax here
  }

...Rather than having a be a prototype-level property, would it be better to have it be an instance-level property? The main advantages of this are 1) clarity when skimming code, because you don't have to read the constructor to discover the instance properties, and 2) similarity to other languages - namely C++ and Java - which makes JS more intuitive. Though I realize similarity to other languages may be a non-goal, I've definitely seen people run into issues here when working in JS frameworks that provide classes, particularly with array properties on prototypes. The obvious disadvantage is that then an alternate syntax would be required for prototype-level properties.

@dherman: typo, or failure to go with flow. There are two flows, the comma-force is weak if the element the comma follows is not a property initialiser from current object literal syntax, but something more like a declaration. Not fixed, want to noodle on this longer. Requiring "private name, private health," is pretty bad.

@mythz: see @dherman's comment just before yours, and my reply above.

@kevingadd: there's no parsing problem, the grammar is unambiguous. @allenwb proposed optional commas after method definitions in ES.next object literals already. I find the mandatory commas more confusing, although you can put 'em in if you like. Leaving them out helps people used to classy languages where methods dominate the class body and do not require comma separation.

/be

@etlovett: you make a good case for var or whatever making instance not proto-prop -- esp. with mutable initial value. But the counter-arguments remain pretty strong: 1) looks like methods ought to close over it; 2) can't init from constructor parameters or other constructor-local state.

I'd almost rather lose proto-data-property declarations. They are unusual and unnecessary in my view, even with all the leather and batteries included.

/be

@BrendanEich Can't you get around those counter-arguments with syntax like the following?

class Monster {
  constructor (@name, health) {
    @hitPoints = health * 2; //for some reason
  }

  // the following are instance-level properties
  @name // this could be optional, but I would always include it for clarity
  private @hitPoints
  @numAttacks = 2 // or use : instead of =
}

The rough equivalent of this in ES 5 would be:

function Monster(name, health) {
  this.name = name;
  this.hitPoints = health * 2; //obviously this is no longer private
  this.numAttacks = 2;
}

What about a protected modifier (as in C++/Java)? Ive never seen that discussed in a JavaScript context. Is it needed at all? I know a lot of programmers coming from C++/Java would ask for it though.

@bni: The cool thing about private names is that you can pass them around:

import myPrivateName from MyModule;
class Sub extends Super {
    constructor() {
        super.constructor();
        this[myPrivateName] = "abc";
    }
}

That should give you most of the abilities of protected. It’s a tiny bit more work, but it’s so nice that it’s still the same powerful mechanism and that that mechanism feels very JavaScript-y.

see @jashkenas quote at http://brendaneich.com/brendaneich_content/uploads/CapitolJS.011.png and read about grammar validation issues with arrows.

Well, block lambdas in that case:

class Fox extends Animal {
    static const CONSTANT: 42,
    static classMethod: { |a, b, c|
        ...
    }
}

I'll resist the urge to champion either since it's kind of OT. Either way, my point is just that I think we can reuse the solution here.

Also, is there a reason we can't use @ for private definition as well as access?

class Monster {
    @attackHelper(target) {
        ...
    }
}

Not only does this cut down on typing, it reinforces the connection between definition and access—something not at all intuitive when using the private keyword. As an added bonus, if we drop the "private" keyword altogether we lose some of the baggage it brings from other languages. This syntax works especially well with the object literal style:

class Monster {
    @attackHelper: function(target) {
        ...
    }
}

@etlovett Semicolons are already optional in JavaScript, so you could drop all of them from all the examples @BrendanEich posted.

Also, that snippet you pasted makes no sense. That's a function expression, expressions don't terminate in semicolons in JavaScript, ever. Statements can't happen inside an expression.

So, var x = function(){ }; is a variable declaration statement with a keyword, followed by an identifier, followed by the assignment operator, followed by a function expression, followed by the end of the statement, which could be either a semicolon or a new-line — given the next line didn't start with any of + - / ( [.

@BrendanEich I love the <slot> ::= <identifier> ":" <value> { "," <slot> } syntax. But I think a series of slots should be terminated by a semicolon, not a comma:

// instead of
r: 1, g: 1, b: 1,
method() { ... }
// you'd get
r: 1, g: 1, b: 1;
method() { ... }

The former looks too much like mixing class literals with objects literals, and it looks ugly to no end.

I like this object lit based approach a whole lot better (no surprise). I would also want to add const/private to regular obj lits to keep everything symmetric.

Why const? What about:
#k: 42,
as you proposed at last meeting?

Also, still don't like static -- should keep exploring that space.

Finally, dislike having both <| (by what ever name) and extends as different ways to express "the same thing" in different contexts. This will lead to confusion.

@allenwb I find grawlix modifiers a bit cryptic in this case, and out of place alongside the keyword modifiers. That might be just me though. Though is # already in ES.next as an Object literal modifier for setting flags?

static is meh, indeed. But people seem to be using it a lot. And I'm sure people coming over from classical Languages would expect it (even Python allows it by the @staticmethod decorator).

I agree on the issue of <| versus extends. But then, as far as class literals goes up until now, they are a also a different way to express the same thing in a different context, which is what I think most non-classy people — me included — are against in the declarative notation. I'm a purist though :3

None the less I think the familiarity of classes as a subset could still be worth it to some people. Would <| be accepted over extends or would it be the other way around (is extends already reserved btw?). We could try and get input of other developers on that.

@allenwb: +1 to everything mentioned. The # for const-ing would be cool.

extends versus <|: How about the following?

let Sub = Super <| class {
    constructor() {
    }
    foo() {
    }
}

Surprisingly, the grawlix <| works better here than a keyword such as beget or proto. Analogously to functions, one could still allow class Super { ... }.

On static: I agree with Jeremy’s assertion that static is comparatively rare and does not have to be supported by special syntax. You can always do:

class C {

}.{
    #CONSTANT: 123
    classMethod() {
    }
}

I find that very readable.

Interestingly, we are getting to the point where a class declaration is virtually indistinguishable from an object exemplar (except for the class keyword). I like that. Note that that’s an argument in favor of object exemplars: If class declarations are what people expect (are familiar with) then so are object exemplars.

  • Pro class declarations: backwards-compatible.
  • Pro object exemplars: no need to introduce special syntax.

@rauschma I like the <| syntax, with class being just an expression — then this is where I think the class keyword feels even more out of place.

I'm not sure the "monocle-moustache" operator to define "static" properties is a good thing. It doesn't feels declarative enough for me, and it's a weird flavour for people coming from classical languages, imho.

And suddenly, I don't feel # out of place. In fact, it blends in nicely :3

/me is all for object exemplars though.

Where I part company is on reusing the object literal. It is not the syntax
most classy programmers expect, coming from other languages. It has annoying
and alien overhead, namely colons and commas.

I sometimes wonder whether automatic colon insertion in object literals would be such a bad idea.

@kildream
@rauschma

I like: Super <| class {...

I have played around with:
class Sub = Super <| {...
where class is a declaration not an expression element. However, I think I like the expression form better and it solves some issues with the declaration form that I was still grappling with. Some people may complain about the let Sub = but to fit all the essential requirements we need to make some compromises and this seems like a reasonable one.

I also think that the relative frequency of class properties ("static", yuck) compared to instance properties justifies a little extra syntax to express them. I think the mustache formulation would be fine. However, I also have two other possibilities:

class C {
   constructor () {
   }.{
      #CONSTANT: 123
      classMethod() {
   }
   anotherMethod () {}
}

This moves the mustache containing the class properties inside the literal body and directly associates it with the constructor declaration. Alternatively

class C {
   constructor () {
   }
   anotherMethod () {}
   constructor.{
      #CONSTANT: 123
      classMethod() {
   }
}

This assumes that
propertyName mustache
can be used in a property assignment position within an object literal. The meaning is that the properties in the mustache are added to the current value of the named property. In the general case it might be used as:

myObj.{
    prop1.{
        a: 1,
        b: 2
    }
    prop2.{
       addedMethid () {}
   }
}

However, in a toplevel object (or class) literal it adds the properties to the value of the property as defined in the literal. For example:

//the following mean the same thing:

let obj1 = {
      a: someObj,
      b: 2,
      a.{
          method1 () {}
          method2 () {}
      }
};

let obj2 = {
      a: someObj,
      b: 2
      }.{
        a.{
           method1 () {}
           method2 () {}
        }
     }
};

@BrendanEich and private/const/static prefixes are a struggle (do they also work in object literals? If not why not? etc.)

What he said:

@allenwb I would also want to add const/private to regular obj lits to keep everything symmetric.

It's not even about symmetry. I think the whole point of @jashkenas proposal is that using object literals for classes is actually doing "prototypes as classes" with a class prefix instead of modifying the behavior of new. That's what feels very JavaScript-y and easy to grok. It should be mandatory that everything that works on a class body should work on an object literal because the class body is the (annotated) prototype object. Yes, static breaks that concept, but that's the only thing that has a meaning in a class literal and not in an object literal.

Updated, stay on target, new comments welcome.

@juandopazo: agree the idea of object literal extensions applying to class bodies is more than symmetry per se. If we have classes with object literal bodies, then we don't need exemplars and new checking for .constructor. Check me if I'm misreading you.

/be

I don’t understand the public modifier in front of flair and why flair is written as @flair in the constructor (like a private name).

@BrendanEich

I really like this current form, my only gripe is the use of commas for line terminators. IMO blocks { .. } or semi-colons to delimit line statements is more consistent/intuitive:

class Monster {
    isAlive() {
        return this.health > 0;
    }                                                //block delimiter
    numAttacks: 0;                                   //semi-colon delimiter
    r: 1, g: 1, b: 1;                                //commas to delimit items in proto 'declaration group', semi-colon to delimit line/declaration
    const attackMessage: "The monster hits you!"     //semi-colon optional for last declaration
}

I read somewhere earlier that commas are optional, could you clarify it that's still the case for this latest proposal?

Something still feels unbalanced about private names. Suggestion – instead of:

class Monster {

  private { name, health }

  constructor(@name, @health) {
  }
}

write:

class Monster {

  private { @name, @health }

  constructor(@name, @health) {
  }
}

It would be nice if the above notation made private unnecessary: That is, you declare a name as private merely by mentioning it prefixed with an @ inside a class declaration. But I think it would clash with private prototype properties. On the other hand, if non-method prototype properties were forbidden...

Do we get similar sugar for normal instance properties in constructors?

class Point {
    constructor(this.x, this.y) {
    }
}

@rauschma

Agreed, don't think we should even allow the public keyword, as everything by default should be known to be public. Having the public keyword at all adds noise and insinuates that items that aren't decorated as public are 'less public'.

EDIT: re-adding since it wasn't obvious

atm:

class Monster {
    constructor(@name, @health) {}
}

de-sugars (like CoffeeScript) to:

class Monster {
    name, health,
    constructor(name, health) {
         this.name = name;
         this.health = health;
    }
}

FWIW I don't think '@' should be used to declare 'private visibility' and as a shortcut for 'this.' accessor.

I'm +1 on no public keyword. It's public by default; no need to inspire "always explicitly mark as public" conventions.

Obviously, I also agree with using @ instead of the private keyword in definitions. Like I said above, it enforces the connection between the definition and access in a way that the private+@ pair doesn't. (Nobody would look at something defined with the private keyword and think "I can access that with @" or vise versa.)

@mythz: Thanks! @ indeed should not be overloaded (that is, it should only be used for private names). Not being too familiar with CoffeeScript, the constructor argument sugar made no sense to me at all, while everything else is easy to understand. IMHO, constructor(this.x, this.y) is a better choice. I value consistency/uniformity above brevity (in most cases) – we tend to read code more than to write it.
public makes a little sense for declaring instance properties. But I think adding instance properties should be confined to the constructor. I don’t see the need to change that particular JavaScript-ism.

To clarify, I actually prefer CoffeeScript's use of '@' as a short-cut for 'this.' and as such the constructor(@name,@health){} shorthand IMO is nice and intuitive.

What I wanted to say was that I don't think it should be overloaded to mean 'private visibility' as well.

Did somebody propose overloading it? AFAIK, Brendan's proposal is using it solely for private lookups.

Looks like I was mistaken. Yeah, overloading it like that is not good.

@matthewwithanm I’m not 100% sure, but I don’t see how the code makes sense, otherwise.

@rauschma Yeah you're right. For some reason I had gotten it into my head that we were moving away from CoffeeScript's use of @ and instead using it only for private lookups. That's certainly not what's going on in the example though.

I like CoffeeScript's use of @, but if I'd be happy to continue to type "this." if it meant we got some sane private member syntax.

@rauschma, @matthewwithanm, @mythz

Brendan is trying out the public modifier at my suggestion (on the es-discuss mailing list), and since he doesn't seem to be here right now, I'll try to explain...

In this updated proposal, the @ no longer flags private property names: it indicates shortcut access to instance properties without having to type "this.". In order to make this work, however, those instance properties have to be declared in the class body. If you use private, then you get a private property via a Name object. If you use public, then it is an ordinary property, but you can use the @ shortcut. Properties are all still public by default. You don't have to declare anything public if you don't want to. But if you want to use the @ shortcut, you have to use public or private. I think it is really nice to be able to elide the 'this' prefix before property accesses. And it seems a shame to limit that to only private properties.

Speaking for myself, this feels like a win. Like Java, we can refer to instance variables without a this prefix. But better than Java, it is easy to keep instance variables (@ prefix) separate from local variables (no prefix). The @ is pleasantly reminiscent of Ruby.

@rauschma: the whole point of 'public' declarations is to enable typo-safe @foo shorthands in expressions. But you need a declaration to be typo-immune. Keywords prefix the declarations. Using @ mixes declaration and expression context and is ambiguous if @foo can be public as well as private. A public name is a plain old string-equated property name but declaring it public enables @ to access it.

@mythz: class elements default to public, that's true. The point of public x and public {x, y} declarations is to introduce new names into "@-scope", i.e., usable after @ as prefix and .@ binary infix operator. The "name, health," line before constructor serves no purpose except to declare those names, but with private (which CoffeeScript lacks) it's not enough to bind @name and @health. If you don't want @ shorthand, just assign this.name and this.health.

"FWIW I don't think '@' should be used to declare 'private visibility' and as a shortcut for 'this.' accessor."

I agree @ should not be used to declare anything. It is however a shortcut for accessing certain instance variable names. @davidflanagan's suggestion enables those to be private or public names.

@matthewwithanm et al.: i'm not overloading @. It accesses a name foo in a space, and in unary form from implicit |this| base object, that's different from the space that dot accesses. But the private vs. public (string-equated) type of the property name is independent of @. It depends only on whether foo was declared via private foo or public foo.

Yes, using @ to declare would be shorter and it would mirror the expression form of @ (the unary prefix form, anyway). But it would fail to denote private vs. public. So @ would be only for private, as in previous revisions, or we would need another way. Instead, I'm using keyword prefixes public and private as binding forms in class and object literals, scoped to the body and hoisted. Such declarations should not use expression operators as terse as @, and need to distinguish public from private.

/be

@davidflanagan

I like the proposed behaviour but not the existence of the public keyword, I think it should take effort to opt-out of the default public scope.

To play the terse side of the dialectic, we could dispense with @-space declarations, and have @names for public instance properties, as in CoffeeScript, and _names for private, as in Dart. That's restrictive compared to identifier lexical syntax in JS today, though, and I don't hear anyone truly asking for _names to be private by definition.

Declaring public and private names that can be used with @ may be too declarative for some of you. But unless you want to fall back on |this.| prefixing, we need declarations or _ as well as @. I don't think punting private instance variables is the right move. This is the batteries-included argument.

/be

@mythz: "I just can't see what benefits having the public keyword provides."

What would you give up? @publicName (rather require this.publicName), or no public declarations and @publicName is inferred to be public if never seen and no private declaration of publicName is in scope? What if there's a private publicname in scope but the user forgot to shift the N?

Declarations can be helpful. With JS since day one, I did not require any declaration of foo for obj = {}; obj.foo to evaluate to undefined. But this is the source of many bugs, as well as the basis for object detection. For classes, can we do better?

/be

@BrendanEich

Is '@-scope' a different scope to 'public scope'?

Are you also saying this is not valid?

class Monster {
    constructor(@name, @health) {}
}

i.e it needs?

public name, health,

The "name, health," line before constructor serves no purpose except to declare those names.

What is the purpose declaring a variable name, if not to make them public also?

One-line followup: Ruby does not let you access nil for a typo'ed ivar name. You have to declare, and any access of an undeclared method or ivar is an error.

/be

@mythz: let's hold off on imputing a public declaration for constructor(@foo){} in a class for a minute. To answer your first question, @-scope is the scope for identifiers used on the right of @ in method definitions (in class bodies or object literals). The identifier to the right of @ could name a private or a public property in this proposal. It's not only for one of private or public properties and not the other. We want @-expression shorthand for both. See @davidflanagan's recent comments.

This gist does not throw away private, nor does it assume public in the absence of a declaration. Perhaps it should assume public, though (my opening let's hold off... plea).

/be

@BrendanEich

My thinking is that:

name, health,

means:

public { name, health }

And the only variables that don't need to be declared are the ones in the constructor parameters, i.e.

class Monster {
    constructor(@name, @health) {}
}

would implicitly declare public class variables and be equivalent to:

class Monster {
    public { name, health }
    constructor(name, health) {
        this.name = name;
        this.health = health;
    }
}

@mythz: I think I'm with you on constructor(@foo) implying public foo, but I don't see just "foo," in a class body or object literal as enough to bind an identifier in @-space. All such a property assignment does is create an object property. No binding in any lexical identifier space.

If you disagree, then you're saying we must tolerate @foop (misspelling of foo) evaluating to undefined instead of being an error. I think that's a mistake (see my Ruby citation). For dot it's too late and we have the curse/blessing of error-free evaluation to undefined in the case of a typo. For @foo and other.@bar we have the chance to catch typos from day 1.

/be

If you disagree, then you're saying we must tolerate @foop (misspelling of foo)

Can you provide an example where @foop would not be valid? i.e. I think all class @-scope variables should be declared in any of these ways:

class Monster { //@-scope, I prefer 'class scope'
    foo, bar,  //implicitly public
    private { name, health } //private
    const private cname, //private const
    const pname, //public const
    constructor(@iname, @ihealth) { //implicit public declaration
        @foo = 1; //valid
        @iname = 2; //valid (declared in constructor params)
        @foop = 2; //error not declared anywhere
    }
}

@BrendanEich And if all the above is valid, when/why do we need public? What can't we do by not having it?

I think the whole @ idea is great on its own, but it is in competition with existing property access and thus becomes too powerful a feature.

Alternate proposal: Private names (private name object references?) are prefixed by @, private name objects come into being via a corresponding private name (optional: having to declare them, but I don’t think it’s necessary).

class Monster {

    constructor(name, age, health) {
        this.name = name;
        this@age = age;
        this@health = health;
    }
    // sugared: constructor(this.name, this@age, this@health)

    getStrength() {
        // stronger with increasing age...
        return this@age * this@health;
    }
}

The above is a bit more to type, but I think it better complements the current way of property access.

@rauschma

I find

this.@age = age;

counter intuitive to mean this.this.age = age;
i.e. When did @ mean private?

    return this@age * this@health;

I also think this looks weird and prefer @davidflanagan's suggestion of:

    return other.@age * other.@health;

In order to make this work, however, those instance properties have to be declared in the class body. If you use private, then you get a private property via a Name object. If you use public, then it is an ordinary property, but you can use the @ shortcut. Properties are all still public by default. You don't have to declare anything public if you don't want to. But if you want to use the @ shortcut, you have to use public or private.

I very much dislike this. It creates two classes of public properties (default, keywordless public and @-accessible public) and the @-scoping feels like an unexpected side-effect of a scope keyword.

Declaring public and private names that can be used with @ may be too declarative for some of you. But unless you want to fall back on |this.| prefixing, we need declarations or _ as well as @.

I think either of these approaches—"this." prefixing or _private declarations—would be preferable to muddying the waters with two differently-defined and differently-accessed classes of public properties.

The most important part of the solution is that there be a clear way to define and access public and private members, and I feel like the current proposal ends up sacrificing that clarity to get the @ shorthand syntax—which is something we all want, but not worth it if this is the price.

@mythz: See edited version. I replaced this.@age with this@age.

@mythz @rauschma's "alternate proposal" is doing away with the CoffeeScript @-as-"this." and instead using it as a private member signifier similar to earlier in the discussion. (Whether he uses "this.@age" or "this@age" isn't really important.)

@matthewwithanm: I like that I can look at an identifier and immediately tell that it is private. Similar to Dart’s underscore-prefixing. I’ve toyed with the (less grawlixy) idea of declaring which identifiers are private (similar to how Brendan’s @-proposal works), but that doesn’t feel right.

I like that I can look at an identifier and immediately tell that it is private.

Is this new? i.e. What mainstream languages is this in? the norm is just to use a style convention like '_' prefix to mean private.

On that note, why can't we do away with private keyword as well and just have the '_prefix' naming convention to mean private? less verbose (what I like) and instantly identifiable (others like).

@rauschma I agree; I was proposing the same thing above. But I think there's a pretty strong push to use the @ for attribute access (given the Ruby/CoffeeScript precedent) so I'm not sure if that's going to play out.

If the CoffeeScript @ is to be retained, we need another way to signify privacy. Either a keyword or—as @BrendanEich mentioned—another special character like the underscore.

@mythz Yeah, but private-by-convention doesn't give us any of the benefits of privacy—for example, preventing collisions with properties on other classes in the inheritance chain.

What variable class declarations would look like without public and private keywords:

class Monster {
    foo, bar,         //public
    _name, _health,   //private
    const _cname,     //private const
    const pname,      //public const

    constructor(@iname, @_ihealth) { //implicit public and private declaration and assignment
        @foo = 1;     //valid
        @iname = 2;   //valid (declared in constructor params)
        @foop = 2;    //error not declared anywhere
    }
}

IMO that's much easier to grok, more conventional and need to remember less about variable declarators.

@allenwb: I don’t like either of the two alternatives.

  • Adding class properties to the constructor (constructor() {}.{...}) requires knowledge of the internals which shouldn’t be necessary.
  • The duplicate name (constructor.{...}) solution might overload the extend operator too far: I really hope the other variants make it into ES.next. But I take it that they are already facing opposition, so this additional variant might make matters worse. Furthermore, the same argument as before applies: You would need to know that class === constructor.

Extending the “class object” (actually: the constructor function) via class {}.{...} feels logical to me. That being subjective, we shall see how things develop.

@mythz:

Is this new? i.e. What mainstream languages is this in? the norm is just to use a style convention like '_' prefix to mean private.

I’m a big fan of privacy by convention. But until recently, I never considered name clashes – which can happen in normal subtyping but becomes even more of a danger with traits (which might be in ES.next.next).

On that note, why can't we do away with private keyword as well and just have the '_prefix' naming convention to mean private? less verbose (what I like) and instantly identifiable (others like).

I agree, I propose to use @ for that purpose. I personally never felt the need to abbreviate this., so wouldn’t want any sugar for it.

I agree, I propose to use @ for that purpose.

The problem is @ has never been used for that purpose and is already used to mean something different, whilst '_prefix' is a common code-style convention for private vars.
I like @ shorthand because it saves a lot of 'noise' since this. access/assignment is going to be used a lot more times in a class than the 1-time private name, declaration.

@mythz: I have to say https://gist.github.com/1332193#gistcomment-60814 is pretty sweet. If we reject @foop because no such property was declared in the class body, then we catch typos.

However, the rule that method definitions and property initialisers alike define prototype properties falls down. You've made foo, bar, etc. declare instance properties that can be used after @. Would they appear on Monster.prototype with values undefined (but observable via Monster.prototype.hasOwnProperty('foo'), e.g.)?

/be

@mythz: On _ for private: it's concise as hell but not compatible with the language so far. Not everyone uses the convention. Also, CoffeeScript @ is a consideration but not the only one, and we can make @foo and obj.@bar work differently in JS than in CS if we choose to for good reasons. IOW, Ruby precedent (which favors instance private @) isn't overriding as normative, but neither is CoffeeScript precedent. Both are informative.

/be

I love Coffeescript's @bar for brevity and clarity. Alternatively, how about .bar for private, in keeping with the foo.bar syntax for object member lookup? Downside: it's visually subtle (but not with syntax highlighting).

@tnovelli That doesn't play nicely with the ASI though.

@killdream You're right, it would break the pervasive jQuery-style chaining idiom:

$("#foo")
.flash()
.fade('medium');

+1 @foo as shorthand for this.foo

-1 batteries included... and I'm an ex-Pythonista. I vote for KISS. Whatever the ES standards committee foists upon the world, I don't expect to love it so I'll continue to use a transpiler. :)

@tnovelli

Whatever the ES standards committee foists upon the world, I don't expect to love it so I'll continue to use a transpiler.

I don’t like the tone. How is it foisting if we are having a discussion about it (in which you are currently participating)? It will be impossible for them to create something that is liked by everyone, but at the very least we can appreciate the work they are doing.

@BrendanEich

I don't see how it can't work since it's just a shorter alias for the proposed private { name, health } proposal, i.e. what will work for private should work for _convention. How you implement the behavior is up to the JS engines, my guess was that private variables would live in an 'inner class' private module scope that's not apart of the property chain. @ would look at either proto chain or private scope, i.e something like:

class Monster {
    _priv, pub,    
    getPrivate() { return @_priv; }
}

Would translate roughly to:

var Monster = function () {
    var _priv;
    var f = function(){}, p = f.prototype;
    p.pub = null;
    p.getPrivate = function() { return _priv; }
    return f;
}

On the issue of private vars I'm leaning towards @jashkenas no privates policy at: https://mail.mozilla.org/pipermail/es-discuss/2011-November/017872.html

Since even in languages like C# / Java you can still access private properties using reflection, so if you go further than this you'll stand to make JavaScript more rigid than the rigid languages that's calling for this feature :)
In this light the _private could roughly translate to non-enumerable?

@tnovelli I find it rather unlikely for an embeddable language to have a huge standard library like Python has...

I'm really unconformable with where the whole public/private/@ discussion seems to be heading. Specifically, the push seems to be to make @foo mean this.foo regardless of whether or foo is a public or private name. (See flanagan's https://gist.github.com/1332193#gistcomment-60790 ). However, that seems to ignore the fact that private properties are identified using private name object values and that such values are (or at least may be) stored in regular variables. In particular, the private names used to access properties will not always be defined within the same object/class literal as the reference.

Consider:

const friendChannel = Foo();   //foo returns Name.create() but we can't see that

class A {
    @friendChannel() {}   //is the @ necessary? if present does it introduce a new private name that shadows outer friendChannel?
     work(obj) {obj@friendChannel()}
}

class B{
    @friendChannel() {}
     work(obj) {obj@friendChannel()}
}

Within either class, when the compiler see @friendChannel (meaning this.friendChannel) how does it know whether to use "friendChannel" or the lexically scoped value of the friendChannel variable binding as the property key?

[ ] access also seems to be messed up:

const p = 42;
class C {
    private {p}
    p: 43 //or what every shorthand we use for defining a private property
    method() {
        self = this;
        return self[p /*????*/] //how do I actually get the private name value of p? self[p] is self[42], self[@p] is self[43]
    }
}

These aren't my only issues, but they're a good start.

I think Brendan's latest update with foo.@bar (and @bar meaning this.@bar) as private name property access is great. This other direction seductively leads into a tar pit.

BTW, I still stand by my comments and @rauschma followups regarding "const", "static" and having both <| and "extends" see
https://gist.github.com/1332193#gistcomment-60555
https://gist.github.com/1332193#gistcomment-60569
https://gist.github.com/1332193#gistcomment-60615
https://gist.github.com/1332193#gistcomment-60815

@rauschma, I'm not sure I follow you "knowledge of internals" comment. JS programmers need to know that methods are functions are objects so adding properties to a
via .( doesn't seem to involve internals.

@mythz: sorry to be unclear, _ is easy to make work by brute force, no question. My point about compatibility was that the full JS grammar allows identifiers to start with _ and people use leading _ for other than private today. So while we could elaborate the lexical grammar to define PrivateIdentifier as starting with _, and define that to have new meaning in new contexts (class bodies), giving leading _ new meaning in property names -- and inside function expression bodies -- within object literals is backward-incompatible.

If we are doing private, it'll use private name objects. No fake private. Enumerability has nothing to do with it.

The politics of @jashkenas's post are all wrong -- "freedom" means I can build a fence around my yard, or a wall inside my own house for my personal privacy reasons. The idea that JS must be totally mutable and dynamic is already false: closures are a counter-example. Anyway, this is not the place for bad politics. My proposal is batteries-included and it'll stay that way.

/be

@allenwb: If you use

class {
    constructor() {

    }.{
        classProperty: 123
    }
}

then you would have to understand that the constructor is the class. If, however, you write

class {
    constructor() {
    }
}.{
    classProperty: 123
}

then it is immediately obvious that you are adding something to the class.

My current gist self-critique:

(1) Optional comma suggests significant newline and will lead people to leave comma out after a data property initialiser.

As I've written and said in the past, my only regret with ASI was that I didn't use significant newlines throughout the grammar, but without requiring \ continuations for expressions spanning multiple lines where an operator ends the non-final lines. However, I'm not ready to push for significant newlines in class bodies and object literals. It would need to be a separate strawman, well-thought-out in context of the full grammar.

So the problem remains: commas everywhere, or optional after braced special forms including prefix-keyword groups and method definitions?

(2) I hear the objection to public as a prefix-keyword required by the current gist to add a public name to @-space. The only alternative that works is @mythz's: populate public @-space from the set of non-private-declared property names defined by elements of the class body or object literal, and only those names. No @foop typo if only foo:42, is a property initialiser.

So public could be optional, or deferred (not legal syntax yet). But my question to @mythz still stands: if foo:42, in a class body defines a prototype data property named foo with initial value 42, what does just foo, do? It ought to define a prototype property foo with initial value undefined. If so, @foo in a constructor can be assigned to shadow the proto-property, but is the proto-property even wanted? I think not.

Some designs make a by-fiat distinction between methods and data "fields". Methods go in the vtable (Dart, etc.) or prototype (JS). Data definitions induce instance variables. We could do this but it breaks symmetry between class bodies and object literals.

(3) Point (2) leads me to continue to believe that object literal is not the exact "body plan" we want for classes. If it is, we may have usability problems, now or in the future, and if in the future, we may be unable to fix them due to the reuse of ObjectLiteral grammar. Classes are something new. You see this even in CoffeeScript, which has more special forms under its class body syntax than under its object literal syntax.

So the current gist may have swerved too hard back toward object literals. I know, I'm vibrating on this point. That's part of this kind of far-seeing/many-factors design process. I will attempt to stabilize.

/be

@killdream: "batteries included" in context does not mean "huge standard library". It does mean less minimalism, more leather (@ shorthands, class-side inheritance [static], etc.).

/be

But my question to @mythz still stands: if foo:42, in a class body defines a prototype data property named foo with initial value 42, what does just foo, do?

My personal preference would be to initialize all uninitialized properties to null, i.e desugars to:

foo: null,

I should add that's what C#/Java does, and in C# all reference types or nullable value types initialize to null.

BTW I prefer 'commas everywhere' to significant new lines in JS. My ideal pref would be semi-colon line delimiters (IMO it's more natural), but I'd still be happy with commas.

@BrendanEich 'So the current gist may have swerved too hard back toward object literals. I know, I'm vibrating on this point. That's part of this kind of far-seeing/many-factors design process. I will attempt to stabilize.'

+1 for a pragmatic approach and not destroying the web for 10-15 years

@BrendanEich "So public could be optional, or deferred..."

I guess I don't understand this "public" business at all. To me, this all seems very straightforward:

class Foo {
   private {a,method}
   @a:42,
   @method() {return @a}
}

"a" and "method" are lexically scoped to the class/obj literal body, hoisted to the top, and initialized to a new private name.

The following is a shorthand an alternative expression for the same thing:

class Foo {
   private @a:42,  //this both introduces a new private name binding and defines a property
   private @method() {return @a}
}

If you need to use an externally provided private name you just do the following:

const friendName = Name.create();
const trademark = Name.create();

class Foo {
   @trademark: true,
   @friendName() {return @a}
}

but if you explicitly say "private" in any form you shadow the outer name:

const friendName = Name.create();
const trademark = Name.create();

class Foo {
   private {trademark};    //we want our own
   @trademark: true,
   private @friendName() {return @a} //also our own
}

You normally always explicitly say private (one way or the other) unless you really mean to get a private name from an enclosing scope.

@allenwb: @davidflanagan explained public pretty well. The issue is not private (I will pick a few nits with what your last comment shows below), but whether we can support @foo for public (string-equated) property names too, instead of requiring this.foo all over.

The gist does not put @ in "private attackHelper()..." as your "private @method() {return @a}" example does. You're right that combining the "private a" and "@a:" naively would keep the @, but it seems unnecessary. If @ is just for expressions, not for property name declarations, we have less grawlix and clear delineation between private declarations and private name-use expresisons.

Where you show const trademark being used after @ in a property name context, you lose the ability to catch typos. Object literals should be able to introduce arbitrary property names. If private names in name: value pairs in a literal must be spelled with leading @, but may reference any prior (const? variable?) binding, it seems to me we already accepted a proposal for that: [expr]: value.

Again, I'm looking for typo defenses via early errors -- not runtime errors.

/be

@BrendanEich "So the problem remains: commas everywhere, or optional after braced special forms including prefix-keyword groups and method definitions?"

I don't see how making the comas optional hurts anyone.

If commas are mandatory everywhere and I write:

let obj = {
        m1() {},
        p: 42
        m2() {}
}

I get a syntax error. If they are optional after methods (etc.) but I write the exact same code as above I still get a syntax error. However, if they are mandatory and I leave one off after a method (already easy to do because of existing semicolon conventions) I get a syntax error. It seems to me that optional is the least error prone of the alternatives. (also, good IDEs can help with these sort of things).

"So the current gist may have swerved too hard back toward object literals." I think the issues you are angsting over are minor compared to the burdan of have to similar but different syntax expressions to for two very similar things (an object described by an object literal/exemplar and object described by a class declaration).

@allenwb: Waldemar Horwat pointed out the trouble that comes from optional commas and [privateNameKey]: value syntax.

let obj = {
    m:function f(){}
    [p](
}

If you suggest using @p instead of [p], we lose computed name expressions, but ok. That dodges one bullet. There are probably others.

Always requiring commas is safer and I bet that's how the hardest cases on the committee will go. ;-)

I'm not angsting. Comments here including @mythz's talk about ; instead of , as separator. There are real usability issues at stake, not easily dismissed. We can run over them with the brute-force solution of using only object literal syntax. That's safe. But if we do that, then commas are not optional.

/be

@mythz: null default cuz C# did that? LOL -- dude, this is JS. Let's not change the default property value so lightly and en passant. Especially not in this gist's proposal!

So long as JS has undefined, null means no object and undefined means no value. So property without value defaults to undefined.

/be

On Nov 3, 2011, at 11:11 AM, Brendan Eich wrote:

@allenwb: @davidflanagan explained public pretty well. The issue is not private (I will pick a few nits with what your last comment shows below), but whether we can support @foo for public (string-equated) property names too, instead of requiring this.foo all over.

Ah! That is even worse! (Sorry David, I usually agree with you...)

As a object-oriented designer, when I write

method() {
        this.foo();
}

it means something very different to me than

method() {
        foo();
}

In the first case I am explicitly thinking that I am delegate back to the "receiver of the method" which may be an instance of one of my subclasses. This is something I want to communicate to future readers and maintainers of the code. In the second case, I am explicitly thinking about calling a lexically scoped function, not a method at all. To have this distinction be remotely controlled (ie, not within sight when I'm ready code) means that for every call I see, I have to hunt down whether or not it is a method call or a regular function call.

Java may have done otherwise, but it has different circumstances (all code is written within class declarations). Also, making it optional very clearly leads to controversy See http://stackoverflow.com/questions/2429062/java-when-to-use-this-keyword and http://stackoverflow.com/questions/725770/should-the-java-this-keyword-be-used-when-it-is-optional . I could also say something about "refactoring hazards"...

self went the other direction, every unqualified reference is implicitly this qualified. I don't really recommend that either, and will point out that self really doesn't make use of lexically scoped bindings as pervasively as JS.

The gist does not put @ in "private attackHelper()..." as your "private @method() {return @a}" example does. You're right that combining the "private a" and "@a:" naively would keep the @, but it seems unnecessary. If @ is just for expressions, not for property name declarations, we have less grawlix and clear delineation between private declarations and private name-use expresisons.

Yes, I could leave @ out of "private method() {}" but I like the consistent use of @ in front of private named property definitions. Consistency is better than contextually special cases. In particular I want to say:

const foo = Name.create();
var obj = {
   @foo: expr
}

Instead of

const foo = Name.create();
var obj = {
   [foo]:  expr
}

when I'm defining properties with externally provide names.

Where you show const trademark being used after @ in a property name context, you lose the ability to catch typos.

If you miss spell "trademark" in (@trademark: true,) it is probably going to be an early detectable undeclared name error. similarly, if you forget to put in the private (either prefixed or private {}) in private @myName () {} you are also likely to to get an early error.

Object literals should be able to introduce arbitrary property names. If private names in name: value pairs in a literal must be spelled with leading @, but may reference any prior (const? variable?) binding, it seems to me we already accepted a proposal for that: [expr]: value.

Yes, but @name: value is a better approach with fewer strange edge cases. In fact, I want to drop using [expr] in the name position of property definitions. It is an unnecessary extra knob. If you really intend for @name to be locally private and leave it off, the unintended aliasing risk is no greater than for:

    function f () {
         //let myName;    Opps, left this line out
         foo(myName);    //either early error, or something string at runtime if myName exists in a outer scope
   }

Using @foo to designate private name property references works very nicely both inside and outside of class definitions. Give that up simply to all allow dropping the "this." from this.foo is going down the wrong path.

Allen

@allenwb: "Ah! That is even worse! (Sorry David, I usually agree with you...)" -- did you misread my @foo or @davidflanagan's comment? The proposal is that

class C { public foo, constructor(){@foo=42} ...}

be short for deleting the public foo, and replacing @foo with this.foo everywhere -- obviously with lots of other @foo uses in the ..., or what's the point?

Note: not foo without an @ prefix -- only @foo.

Separating private name semantics to a later comment, want to clarify this one fast.

/be

@allenwb

You wrote:

In the first case I am explicitly thinking that I am delegate back to the "receiver of the method" which may be an instance of one of my subclasses. This is something I want to communicate to future readers and maintainers of the code. In the second case, I am explicitly thinking about calling a lexically scoped function, not a method at all. To have this distinction be remotely controlled (ie, not within sight when I'm ready code) means that for every call I see, I have to hunt down whether or not it is a method call or a regular function call.

I don't get it. If you see foo(), then it is a regular function call, always. If you see this.foo(), it is a method call, and if you see @foo() then it is also a method call. (You may still need to hunt down whether the name of the method is public or private, of course.)

(did you mean not to reply back to the gist on this?)

On Nov 3, 2011, at 11:58 AM, Brendan Eich wrote:

@allenwb: Waldemar Horwat pointed out the trouble that comes from optional commas and [privateNameKey]: value syntax.

let obj = {
   m:function f(){}
   [p]: v
}

If you suggest using @p instead of [p], we lose computed name expressions, but ok. That dodges one bullet. There are probably others.

I am suggesting that substitution. There my be other bullets, but that applies to all constructs. We need to look for them, in everything we do. But I see no particular reason to assume that there is more likely to be additional ones here then in anything else we are discussing.

Always requiring commas is safer and I bet that's how the hardest cases on the committee will go. ;-)

I don't really object to commas, unless they become the motivation to switching back to statement syntax.

I'm not angsting. Comments here including @mythz's talk about ; instead of , as separator. There are real usability issues here, not easily dismissed. We can run over them with the brute-force solution of using only object literal syntax. That's safe. But if we do that, then commas are not optional.

My main concern about the usability of having different syntax for object literal and class literal body. My mantra is "if you can do it in a class body, you can do it in an object literal". If that holds that I think class literals are fine. If not, I have doubts.

My reply is a comment on the gist. The mail may come out of order.

/be

On Nov 3, 2011, at 12:26 PM, Allen Wirfs-Brock wrote:

(did you mean not to reply back to the gist on this?)

On Nov 3, 2011, at 11:58 AM, Brendan Eich wrote:

@allenwb: Waldemar Horwat pointed out the trouble that comes from optional commas and [privateNameKey]: value syntax.

let obj = {
  m:function f(){}
  [p]: v
}

If you suggest using @p instead of [p], we lose computed name expressions, but ok. That dodges one bullet. There are probably others.

I am suggesting that substitution. There my be other bullets, but that applies to all constructs. We need to look for them, in everything we do. But I see no particular reason to assume that there is more likely to be additional ones here then in anything else we are discussing.

Always requiring commas is safer and I bet that's how the hardest cases on the committee will go. ;-)

I don't really object to commas, unless they become the motivation to switching back to statement syntax.

I'm not angsting. Comments here including @mythz's talk about ; instead of , as separator. There are real usability issues here, not easily dismissed. We can run over them with the brute-force solution of using only object literal syntax. That's safe. But if we do that, then commas are not optional.

My main concern about the usability of having different syntax for object literal and class literal body. My mantra is "if you can do it in a class body, you can do it in an object literal". If that holds that I think class literals are fine. If not, I have doubts.


Reply to this email directly or view it on GitHub:
https://gist.github.com/1332193

@BrendanEich

@mythz: null default cuz C# did that? LOL -- dude, this is JS.

It's got nothing to do with copying, it's got to do with the principal of least surprise and what JS devs expect. If that happens to be undefined - so be it, but since we're making classes it's not a bad idea to replicate the behaviour of other classy languages.

On Nov 3, 2011, at 12:24 PM, Brendan Eich wrote:

@allenwb: "Ah! That is even worse! (Sorry David, I usually agree with you...)" -- did you misread my @foo or @davidflanagan's comment? The proposal is that

class C { public foo, constructor(){@foo=42} ...}

be short for deleting the public foo, and replacing @foo with this.foo everywhere -- obviously with lots of other @foo uses in the ..., or what's the point?

"deleting" -> "defining"??

I think I starting to understand David proposal, (not the same as liking...)

"public" is the replace for his earlier "prop" suggestion, but optional.

I don't like that
public foo:x
vs
foo: x

changers how you can access foo.

I think I starting to understand David proposal, (not the same as liking...) "public" is the replace for his earlier "prop" suggestion, but optional.

Not quite. I was proposing "prop" or "proto" as an alternative to "var" when Brendan was still looking for a comma-free way to declare data properties on the prototype. Things evolved quickly and my "prop" suggestion is no longer meaninful.

My proposal for "public" is strictly by analogy to "private", and to enable the ability to leave out "this"

I don't like that public foo:x vs foo: x changers how you can access foo.

What I imagine is that programmers who want this kind of classy syntax will do in practice is declare all their class members with either public or private and then always use the @ prefix instead of typing this. So yes, there's a difference, but I'm guessing that it won't come up that often for programmers who are using this new syntax.

When I proposed it, I thought that 'private' was just a declaration form like private {x,y,z}, not a modifier that could be used before individual class members. And I imagined that public was the same way. It does seem like a win to allow them before individual class members as well. And if we're doing that, then making declared class members public by default also seems like a good idea. Brendan addresses that upthread: https://gist.github.com/1332193#gistcomment-60988

@BrendanEich I see. I kind of always associate "batteries included" with a huge standard library, but then, I haven't seen the term used out of that context before.

I don't see less minimalism as a bad thing, given such decisions clearly increase the expressivity of the language, without making it overtly complex at the same time.

@mythz: We're not replicating anything from C#. Certainly not C# classes! This seems like another case of the 'class' keyword misleading.

@allenwb: I meant "deleting" as a text-editing operation to rewrite the public/@ code to use this.foo. The |public foo| declaration by itself serves only to bind a @-space variable named 'foo'. It does not create this.foo -- you need a constructor assignment to do that.

/be

Am I the only one that likes JavaScript (relatively) the way it is? maybe this fluff adds value for some people but yikes :s

@visionmedia You could be. Sugar to easily express commonly used patterns is never a bad thing. The option to do it the current way will always be there.

@mythz it is a bad thing, look at c++ it's a huge mess

@visionmedia Please cite a specific example of a bad language feature in C++ added to express a commonly used pattern?

@mythz are you serious??..

Yes. posting here to prevent spamming the list just to repeat myself.

you've worked on c++ projects right?

Yes, using a strawman argument pointing at C++ as a whole as the reason to not to include useful and popular language features does not re-inforce your point. Please cite a specific example.

c++ is the example, derived from C I would expect you know the differences. One is simple, (mostly) elegant, one is a massive clusterfuck beast from hell

You did it again. I'm leaving this circular discussion.

@mythz are you serious??.... you've worked on c++ projects right?

c++ is the example, derived from C I would expect you know the differences. One is simple, (mostly) elegant, has few concepts, few rules, one is a massive clusterfuck beast from hell

@visionmedia Hardly so. I believe a good part of the community is a bit averse to most of the extensions that have been proposed for ES.next. Some of them, however, are needed for the new use-cases of JavaScript. The language has grown, a lot, and so has its user base. For some people or some problems, a certain different set of features is needed — which I do believe class literals don't fit any, but whatever.

And while I would rather have JavaScript be closer to Scheme (R7RS-small) in philosophy, I can't say plenty of the features proposed have became a requirement: standardised module handling; traits; destructuring assignments; better object literals; etc. I still can't see the value in some of them (yes, quasiquoting, I'm looking at you!), though.

@visionmedia: if you want to argue seriously, do it. Otherwise you're just doing sports/beer advocacy of the "tastes great!" / "less filling!" variety, which is noise at best.

I said at the SPLASH keynote Q&A last week, in reply to David Ungar's question, that JS won't grow like C++ has. We use C++ at Mozilla, just as Google and Apple and Microsoft do. C is not an option. We do not subset aggressively but we do avoid some features that are not quite portable, and others that are simply not necessary. We do make heavy use of auto class helpers (RAII), copy constructors, template meta-programming, abstract base classes (interfaces). Anyway, let's get back to JS.

It's one thing to say "I am fine with function", quite another to say "and you should be too". The Harmony agenda is about serving long-standing usability problems that many (not all) users report, without adding new semantics if we can at all avoid it. For classes as sugar on top of the prototypal pattern, this means 'class D extends B' as sugar for the D.prototype = new B; song and dance, and super (which needs detailed and careful design).

If OO JS using prototypes is not something you use, godspeed. But for those who do use it, current .prototype/super ad-hoc and open-coded solutions are too verbose and bug-prone. Saying "don't do OO JS that way" doesn't work. It's either "here is a better way" or "you get nothing more than in JS today". Jeremy along with others including some of us on TC39 are trying for "here is a better way".

/be

@BrendanEich eh? To be honest, Brendan, if you ditch constructors entirely, and include a small set of utilities in the API: basically Object.extend, Object.create and Object.clone (create + extend); it's not really verbose. In fact, with a few utility functions, object composition in JS becomes more or less a Joy. The only thing I still hold a grudge against is the lack of a short function notation for object literals, so one don't need to do foo: function foo(){ ... };

tl;dr; I don't see how (minimalistic) class literals hold any more value than object exemplars.

var foo = clone(Clonable {
  toString: function toString(){ return '[object Foo]' }
})

var instance = foo.copy() // or foo.new() or whatever else

// would become
var foo = clone(Clonable, {
  toString(){ return '[object Foo]' }
})

// Or yet
var foo = Clonable <| {
  toString(){ return '[object Foo]' }
}

var instance = new foo()

Super is a concern, yes. Though I don't think I've needed it much, going with hardcoded object names is a terrible thing. But then, super is not dependent on class literals, exclusively.

Which leaves class literals only worth for a few reasons:

  • Enforced shape of the instances — which I'm not sure why. Familiarity for classical programmers? Optimisations?
  • Possibility of having a nicer syntax to support new features (type guards, super, traits, etc). I believe there's only so much you can do with the object literals syntax before its definition starts to resemble a Perl program.
  • Tooling.

Did I miss something? Because really, I don't see the value of adding new class literals in the minimalistic form, if they don't cover the second use case, as the others are easily achieved through object literals with the newest extensions, with on-par syntax — in terms of flexibility and terseness =/

@BrendanEich so "better" is cluttering a grammar just to save a few key-strokes here and there? From what I've experienced typing is not a bottleneck when it comes to writing software. IMHO it should be visually explicit, so ditching punctuation does not necessarily help there, and while larger keywords like "function" are certainly annoying to type, they're easy to identify. It's not so much that these proposals bug me (aspects do of course), but to me it seems like the grammar aspect is largely geared towards what is trendy now, so what's next? introducing more ambiguity to save even more keystrokes? perhaps I'll just have to hope Lua gets it's place in the browser sometime in the future.

@killdream: I like exemplars too, and (see my intro comment, specifically "To be perfectly honest, I personally would be happy with Allen Wirfs-Brock's "Exemplars" approach and no classes" -- you did read that, right?). It seems to me you're replying in place of @visionmedia here, though, which is kind of a gist foul.

Note, however, that calling functions like clone is not the same as writing declarations or expressions. It's harder to optimize and it requires runtime analysis in general. That's not just hard for implementors, it is hard for users in the large. Also, it is more verbose if you count characters or keystrokes. Count apples to apples.

/be

@visionmedia I don't think the sugar is included for the sake of "typing less", but for making, in fact, programs more explicitly on what they do, as opposed to how they do. Dropping the function keyword doesn't necessarily fit that though, but it removes the clutter from reading — I say clutter because that's already defined by the surrounding context and can be easily grasped, imho. Less clutter usually means that it's easier to read a piece of code. I mean, did you ever try something that has comment annotations everywhere in the body of a function? It still irks me when a function has any comment in the body of a function, but I think that's just me.

@visionmedia: who said anything about "typing"? 'function' especially with 'return' of short expression bodies is 14+ chars of overhead for reading, never mind writing. It's incredibly verbose compared to nearby languages. No one needs to read 14 letters over and over just to make a functional abstraction "easy to identify".

You didn't actually make a particular technical argument. "Typing" is a straw man. Try again, or save it for twitter :-|.

/be

The other thing that bugs me about class literals, is that mid-file you have no immediate clue what you're looking at. Which in some projects is no big deal, class per file, but many projects (like node) include several "classes" in a file. For this reason I really prefer the verbosity of Foo.prototype.bar =, the context is clear.

@killdream sure but that's a hugely opinionated aspect of any language, to some js looks much better than ruby/py/cs in terms of readability, and to some the opposite, not much we can do about that I suppose other than take a real poll of people using the language and not just relying on fanboys.

@BrendanEich for sure, implicit returns would be great for that reason, CS's -> and friends look fantastic in small doses, but I'm assuming you've seen large bodies of code as well? some of us want to gouge our eyes out with all due respect. If it were not a language we all pretty much have to use I really wouldn't care, I would move on to something I consider nicer, but whether you agree or not any opinion is valid, WE are the users, maybe even writing more JavaScript than yourself.

@BrendanEich ah, yes. From what I gather, most of (the active people on) es-discuss seem to favour object exemplars over class literals.

About the verbosity of both approaches, I think the perceived verbosity is likely the same. Optimisations are a tough matter, indeed. But then, there's the awesome triangle operator :3

@visionmedia You could also say most of the aspects of a programming language's syntax are highly subjective. Even in the real poll of people using the language you're likely to find highly divergent opinions on what "feature x" should look like. Just take a look at the endless discussions about ASI, indentation style, brace placement style, and of course, "lol how do I wrote OOP in JS". The end result will be biased at the end, but the interesting thing about these discussions is that you can make them less of a one-man biased. People are always free to chip in es-discuss and, well, discuss stuff.

@killdream if it's so speculative I dont see why there's so much effort involved in changing it. Everyone I know that uses JavaScript daily is absolutely fine with the syntax, perhaps even liking more than most languages they've used. On the other than almost every person I talk to in #node.js migrating over from ruby etc loves coffee-script because it's more familiar to them, I get that, but do we change an existing language just to satisfy people that are not taking any time to really learn the language? and what happens to be trendy now?

@visionmedia: I write JS. Please don't personalize in lieu of actual arguments. The calls for 'class' in JS are something I still find overloaded and even contradictory. I still suspect we won't get 'class' into ES.next. Some in the community will find this to be a terrible failure. You won't, but why are you more important?

Again, the thing that bugs me most is when "freedom" is defined as telling me what I must do in my code by telling the standards body not to extend the language. That's not freedom at all.

If you have to read my code that uses extensions you personally don't use, and this is a bad problem for you, then stop reading (and using) my code. That's freedom -- you don't have to read my code, and I don't have to use yours.

To settle what to add to the language, we need better arguments than subjective ones that you contend are all equally "valid". We need objective arguments founded on common premises about costs to users and implementors of adding vs. not adding, and on how the extensions interact with the rest of the language.

/be

@visionmedia Good point about Coffeescript's implicit returns: I decided to spell out 'return' except in one-liners. IMHO -> is nicer than (function(x,y,z){...}) but there's gotta be a better way to do async programming. Edit: CS has some issues with ambiguity and more-than-one-way-to-do-it (e.g. and and &&), but I like it in general. I like its class syntax. Love everything-is-an-expression. Significant Indentation neatly sidesteps all these comma/semicolon debates... but that's too radical a change for JS/ES6, it should go without saying. Basically, I agree with you 100% about keeping JS simple, and anyone who wants something different can always use a transpiler like Coffeescript. It's pretty painless in practice. End Edit

Anyway, these gist comments are becoming unwieldy and going off topic from the proposal. I say, let @BrendanEich think things over in peace and come back with a revised proposal when he's good and ready. In fact I'd be perfectly happy if he comes back in several months and says here, this is ES6. I trust his judgement.

@visionmedia I for one write JavaScript daily and have lost count of how many times I've wished the language would be more flexible and expressive in some parts — I curse everytime I need to write a library to support more than one JavaScript environment, did you see how much boiler-plate you have to write to support Browsers, Node and CommonJS? It's madness. I'd also like a better way to express object composition, which is what prototypes should be for. I'd also like a better way to write DSLs, which JavaScript alone just won't cut for some problems.

There are other several particular problems I just wish there would be a way to express myself better in the language — asynchronous code, pattern matching, multiple dispatching (I write in a function-heavy style), etc, etc, etc. Just as I struggle to use the language everyday for some particular cases, there are other people who do struggle, for different reasons. They are users of the language too, and they should get better ways of expressing themselves just the same.

As Brendan said so many times in es-discuss, freedom means I should be able to fence my yard or put walls on my house if I want to. It doesn't really affect you — unless you want to drop by uninvited, — you can still leave your house unfenced. That's okay too, because freedom doesn't work just from a single point of view. For which I suppose he meant that the JavaScript community is too heterogeneous. There are several users with different use-cases (yes, actual users of the language). They should have their voice heard just as well as the people who are fine with the language as it is. If we can reach a common ground where one feature that's introduced helps a certain group without making the language a pain to everyone else, I don't see what could be so terrible about introducing that feature — which doesn't mean I'm in favour of featuritis, but oh well.

@visionmedia

Honestly I'm surprised at your anti-language-feature stance for reducing boilerplate since one of the biggest USP's of your popular express framework is that there is minimal typing and nearly no boilerplate. The advertised homepage example exemplifies this:

app.get('/', function(req, res){
    res.send('Hello World');
});

It looks even better in CoffeeScript:

app.get '/', (req,res) -> 
    res.send 'Hello, World'

Noisy repetitive syntax, masquerades the essence of your program and makes it less enjoyable to read / write, and if it's too disjointed and cumbersome to do (like prototypal inheritance) it's simply avoided for small classes. The class sugar in CoffeeScript demonstrates this where there are proportionally a lot more devs creating prototypal classes in a CoffeeScript app than what's done in a JS one, and that's simply a function of it being effortless to do.

@mythz like I said... looks great in small doses, and then you gouge your eyes out...

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.