Minimalist (why) classes ?
Thats exact port of proposal that Jeremy wrote here: gist.github.com/1329619
I could write add examples from the classes proposal if that wolud help: harmony:classes
-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)
On Nov 11, 2011, at 4:09 PM, Irakli Gozalishvili wrote:
Thats exact port of proposal that Jeremy wrote here: gist.github.com/1329619
I could write add examples from the classes proposal if that wolud help: harmony:classes
Maybe, but I think that you'd be beating a dead horse.
Class syntax is wanted to avoid some method calling boilerplate that's more verbose, arguably easier to get wrong, and harder to analyze and optimize. That's it.
Hence, "classes as sugar". If you find existing JS sweet enough, you won't want classes.
On Fri, Nov 11, 2011 at 4:31 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 11, 2011, at 4:09 PM, Irakli Gozalishvili wrote:
Thats exact port of proposal that Jeremy wrote here: gist.github.com/1329619 I could write add examples from the classes proposal if that wolud help: harmony:classes
Maybe, but I think that you'd be beating a dead horse. Class syntax is wanted to avoid some method calling boilerplate that's more verbose, arguably easier to get wrong, and harder to analyze and optimize. That's it. Hence, "classes as sugar". If you find existing JS sweet enough, you won't want classes.
If I understand Iraki's proposal, then, no we don't find JS sweet enough.
Object.extend() does not exist. Similar but not identical functions are widely used. We would like a standard form built-in to the runtime.
Or is this already in the standard but not implemented?
jjb
On Nov 11, 2011, at 4:42 PM, John J Barton wrote:
Object.extend() does not exist.
Which one do you mean?
Irakli is using Function.prototype.extend, not something like PrototypeJS's Object.extend.
Similar but not identical functions are widely used. We would like a standard form built-in to the runtime.
You have to specify. But anyway, adding something specific, say Irakli's Function.prototype.extend, would not address the goals of classes-as-sugar.
Or is this already in the standard but not implemented?
What "this" do you mean, exactly?
No fair acting like I didn't get you a free hot dog while I was selling hamburgers! You have to give (with specifics) to hope to get; you have to play to win.
What I'm suggesting is to sugar current patterns, without adding new syntax similar to how Funciton.prototype.bind was added. And maybe in next iteration add special syntax if it still we be wanted. It's just so much easier to fix Object.extend
if it will end up to be a wrong fit than change a special class syntax.
BTW I have updated examples to illustrate exactly what is problematic requires sugar gist.github.com/1355701
Class syntax is wanted to avoid some method calling boilerplate that's more verbose,
In my examples it actually takes same amount of chars:
class Foo extends Bar {}
VS
var Bar = Foo.extend({})
arguably easier to get wrong, and harder to analyze and optimize. That's it.
Arguably indeed, IMO increasing surface of language makes surface of things that can be made wrong bigger, increasing probability of making things wrong.
-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)
On Fri, Nov 11, 2011 at 4:48 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 11, 2011, at 4:42 PM, John J Barton wrote:
Object.extend() does not exist.
Which one do you mean?
I mean Object.extend does not exist.
Irakli is using Function.prototype.extend, not something like PrototypeJS's Object.extend.
Iraki wrote Object.extend().
Similar but not identical functions are widely used. We would like a standard form built-in to the runtime.
You have to specify. But anyway, adding something specific, say Irakli's Function.prototype.extend, would not address the goals of classes-as-sugar.
True by your definition, but not relevant, since I'm not claiming any goals classes are covered. Iraki is, but I guess he missed your point about our opinion on classes being irrelevant (which makes sense to FWIW)..
Or is this already in the standard but not implemented?
What "this" do you mean, exactly?
Of course I cannot answer "exactly". If I could we would not need a standard.
No fair acting like I didn't get you a free hot dog while I was selling hamburgers!
We need hot dogs. Hopefully someone selling other things will offer hot dogs if we keep asking.
You have to give (with specifics) to hope to get; you have to play to win.
Ok sorry I did not know the rules.
jjb
On Nov 11, 2011, at 5:02 PM, Irakli Gozalishvili <rfobic at gmail.com> wrote:
What I'm suggesting is to sugar
Sugar usually means new syntax, not methods, but no worries.
current patterns, without adding new syntax similar to how Funciton.prototype.bind was added.
That ES5 addition was years late, a bit different from the de-facto standard, and it is still verbose and a bit hard to optimize compared to lexical-only |this| forms such as block lambdas.
Still good to have, but we are not operating under ES3.1's artificial "no new syntax" regime.
And maybe in next iteration add special syntax if it still we be wanted.
Iterations take 3 years anyway, no savings and potentially big tax on community.
It's just so much easier to fix
Object.extend
if it will end up to be a wrong fit than change a special class syntax.
No, it is not easy to fix anything once standardized.
Having written all this, I will repeat that I like your selfish work and the exemplar idea, and I would not want over-minimal classes instead or alongside. Minimizing too much defeats the purpose of adding class syntax. Hence my desire for batteries and leather included.
BTW I have updated examples to illustrate exactly what is problematic requires sugar gist.github.com/1355701
Class syntax is wanted to avoid some method calling boilerplate that's more verbose,
In my examples it actually takes same amount of chars:
class Foo extends Bar {}
VS
var Bar = Foo.extend({})
arguably easier to get wrong, and harder to analyze and optimize. That's it.
Not if classes also provide statics, privates, constructor(@x, @y) {} shorthand, etc.
Arguably indeed, IMO increasing surface of language makes surface of things that can be made wrong bigger, increasing probability of making things wrong.
Not the semantics, though. That is the point of desugaring.
Getting the syntax right, or not adding it till we do, is still required.
On 11/11/11 23:44, John J Barton wrote:
On Fri, Nov 11, 2011 at 4:48 PM, Brendan Eich<brendan at mozilla.com> wrote:
On Nov 11, 2011, at 4:42 PM, John J Barton wrote:
Object.extend() does not exist. Which one do you mean? I mean Object.extend does not exist.
Irakli is using Function.prototype.extend, not something like PrototypeJS's Object.extend. Iraki wrote Object.extend(). `Object' is a function, though. So it shares stuff from the Function.prototype object :3
On Nov 12, 2011, at 4:38 AM, Quildreen Motta wrote:
On 11/11/11 23:44, John J Barton wrote:
On Fri, Nov 11, 2011 at 4:48 PM, Brendan Eich<brendan at mozilla.com> wrote:
On Nov 11, 2011, at 4:42 PM, John J Barton wrote:
Object.extend() does not exist. Which one do you mean? I mean Object.extend does not exist.
Irakli is using Function.prototype.extend, not something like PrototypeJS's Object.extend. Iraki wrote Object.extend(). `Object' is a function, though. So it shares stuff from the Function.prototype object :3
That's right. My point is that Irakli's .extend is quite different from PrototypeJS's, contrary to what John seemed to assume.
IINM Irakli has a gist that defines Object.prototype.extend, which is inherited of course by Function.protoytpe and all functions (unless shadowed or cut off via null proto). But this .extend is not the two-argument one that does "Swiss inheritance" (www.crockford.com/javascript/inheritance.html).
On Sat, Nov 12, 2011 at 11:53 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 12, 2011, at 4:38 AM, Quildreen Motta wrote:
On 11/11/11 23:44, John J Barton wrote:
On Fri, Nov 11, 2011 at 4:48 PM, Brendan Eich<brendan at mozilla.com> wrote:
On Nov 11, 2011, at 4:42 PM, John J Barton wrote:
Object.extend() does not exist.
Which one do you mean?
I mean Object.extend does not exist.
Irakli is using Function.prototype.extend, not something like PrototypeJS's Object.extend.
Iraki wrote Object.extend().
`Object' is a function, though. So it shares stuff from the Function.prototype object :3
That's right. My point is that Irakli's .extend is quite different from PrototypeJS's, contrary to what John seemed to assume.
Irakli proposed his extend() for the same set of use cases covered by PrototypeJS's extend(): creating objects from prototypes. As Irakli says above his defn:
// There is nothing new here, just standardizing what libraries already do // today, in a similar manner as Function.prototype.bind was standardized for // ES5.
Of course his extend() is not Prototype's extend(), or Crockford's or Hewitt's, or Resig's, etc, etc. That's exactly the problem.
IINM Irakli has a gist that defines Object.prototype.extend, which is inherited of course by Function.protoytpe and all functions (unless shadowed or cut off via null proto). But this .extend is not the two-argument one that does "Swiss inheritance" (www.crockford.com/javascript/inheritance.html).
This kind of discussion illustrates my point: JS is not sweet enough because we are missing core operations for constructing prototypes in a standard way.
jjb
On Nov 12, 2011, at 1:07 PM, John J Barton wrote:
On Sat, Nov 12, 2011 at 11:53 AM, Brendan Eich <brendan at mozilla.com> wrote:
That's right. My point is that Irakli's .extend is quite different from PrototypeJS's, contrary to what John seemed to assume.
Irakli proposed his extend() for the same set of use cases covered by PrototypeJS's extend(): creating objects from prototypes.
PrototypeJS's Object.extend, last I looked, was this:
Object.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; };
This doesn't create an object from a prototype at all.
This kind of discussion illustrates my point: JS is not sweet enough because we are missing core operations for constructing prototypes in a standard way.
A PrototypeJS-derived Object.extend is on the agenda, as part of .{ (but different from it -- and different from Irakli's extend).
Let's argue about specifics or we'll get nowhere. Do you think Irakli's selfish.js extend (Gozala/selfish/blob/master/selfish.js) is the way to go, or Prototype's quite different form?
Let's argue about specifics or we'll get nowhere. Do you think Irakli's selfish.js extend ( Gozala/selfish/blob/master/selfish.js) is the way to go, or Prototype's quite different form?
I'd personally prefer Prototype's extend because it actually extends an object rather then creating a new object whom's [[Prototype]] is the extended object.
Even though the latter may be useful, I'd rather have an Object.make or Object.createSimple for that.
On Sat, Nov 12, 2011 at 7:08 PM, Brendan Eich <brendan at mozilla.com> wrote:
snip
Let's argue about specifics or we'll get nowhere. Do you think Irakli's selfish.js extend ( Gozala/selfish/blob/master/selfish.js) is the way to go, or Prototype's quite different form?
Neither of them are fit for standardization. Selfish and Prototype are both incapable of correctly "deep copying" arrays or objects, resulting in new "instances" being able to modify values of the base object's properties if those values are elements of an array or property values of an object - because the array and object property themselves are references, not real copies.
PrototypeJS is painfully obvious, Selfish requires a demonstration. I forked/cloned the repo and added a branch with a set of tests:
The branch: rwldrn/selfish/tree/test
The commit: rwldrn/selfish/commit/ee55af5ec00074005fa74e9d9eb4be4dc4db5163
An Object.extend() that creates an object full of references is certainly not the way to go.
On Sat, Nov 12, 2011 at 4:15 PM, Jake Verbaten <raynos2 at gmail.com> wrote:
Let's argue about specifics or we'll get nowhere. Do you think Irakli's selfish.js extend (Gozala/selfish/blob/master/selfish.js) is the way to go, or Prototype's quite different form?
I'd personally prefer Prototype's extend because it actually extends an object rather then creating a new object whom's [[Prototype]] is the extended object.
We could start by classifying the existing solutions. Perhaps there are better terms, here is my start: "shallow" vs "deep" usual defn: en.wikipedia.org/wiki/Object_copy "merge" colliding properties are resolved by selecting a winner, typically the left most object "flatten" the properties of the [[Prototype]] (proto-linked objects) become 'own' properties of the result "own" the own-properties are used
Prototype's two argument extend(): produces an object equal to the "flatten shallow merge" on the arguments.. sstephenson/prototype/blob/master/src/prototype/lang/object.js#L51
Gonzala's gist's multi-argument extend() produces a function object with a .prototype from "shallow merge own" on the arguments, a prototype chain linking this.prototype, properties from |this|, and a body that creates an object
I agree that calling Gonzala's function extend() might be confusing. We could all it Object.class() ;-)
I don't agree that Prototype's extend() is clearly a winner
Even though the latter may be useful, I'd rather have an Object.make or Object.createSimple for that.
I think the primitive we need to agree on is the copy.
jjb
Good point. Note that you can easily do the same thing without copying, via prototypes: rauschma/proto
Neither of them are fit for standardization. Selfish and Prototype are both incapable of correctly "deep copying" arrays or objects,
Why does it matter that they don't deep copy? Deep copying is a difficult problem that needs to be standardized separately. I've personally avoided deep copying for this reason and don't use it anymore.
One can accept that an extend is merely a shallow copy properties by reference, because this (although limited) behavior is easy to understand.
I have a version of Object.extendRaynos/pd/blob/master/src/pd.js#L82
that is a "shallow own merge".
However having a deep copy mechanism that works without obscure edge-cases would be great. Of course it would be nice if we had the choice of deep vs shallow copy within the API.
It depends on what kind of extends
you mean:
-
Inheritance: Even though data being kept in prototypes is rare, it’s usually better to chain prototypes even when ignoring aliasing problems (no redundant properties, instanceof works transitively). Performance degradation should be negligible due to internal optimizations.
-
Merging objects: Shallow copy is the way to go.
On 12/11/11 19:07, John J Barton wrote:
This kind of discussion illustrates my point: JS is not sweet enough because we are missing core operations for constructing prototypes in a standard way.
Agreed. I've always thought about JS objects as sets, but it feels weird that I don't get disjunction, intersection and other basic operations out of the box.
Basically, I've missed primitives for:
-
Creating objects that share properties from other objects (Object.create)
-
Adding properties to an object (Object.extend)
Object.extend(Object target, Object sources...) → Object (sources.reduce({|tgt, src| Object.keys(src).forEach({|key| tgt[key] = src[key]}) tgt }, target)
Should extend' copy all the properties? Only the "own" properties? I feel
own' properties make more sense.
- Defining new objects that extends on the [[Prototype]], which is basically Object.create followed by copying the properties of one or more objects over to the new instance. (Object.clone? Object.inherit? I never felt `extend' quite says it all)
Object.prototype.clone(Object parent, Object mixins...) → Object (Object.extend.apply(Object.create(parent), mixins))
- Merging (while possibly flattening, as objects can't have several [[Prototype]]s) objects.
Object.merge(Object mixins...) → Object (x = Object.extend.apply({}, mixins))
Dicts are probably a better answer for this one though.
-
Creating instances from objects directly (new Stuff, Stuff.new), though I'm not sure there's much sense in having two different standard ways of doing the same thing. I guess JS is stuck with constructors =/
-
Creating objects by removing/renaming certain properties from other objects, which is useful for object composition. Though I guess this would be handled by traits(?)
Of course, you still need a better syntax to express it all, and the proposed extensions to object literals come close to minimalist classes:
var foo = Base.clone({ method1() { }, method2() { }, get stuff() { } })
var foo = Base <| { method1() { }, method2() { }, get stuff() { } }
class foo extends Base { method1() { } method2() { } get stuff() { } }
That is, if we disregard private and other modifiers. If such modifiers were added to the Object literal (meh?), it would be quite the same thing. Except a full-blown declarative class syntax could be better optimised and would allow for a sugary syntax if/when traits/type guards are added.
var foo = Base <| { #constant: 1, // or const @private_stuff: bar, // or private method1() { }, method2() { } get stuff() { } }
class foo extends Base { @private_stuff: bar; // or private #constant: 1; // or const method1() { } method2() { } get stuff() { } }
Except I'm not sure the @private could be made to work without creating confusing semantics around it, so object literals would still be one step behind.
Should
extend' copy all the properties? Only the "own" properties? I feel
own' properties make more sense.
I agree "own" makes more sense, we also want to specify whether we want to extend "enumerable" properties. Your example does not.
- Defining new objects that extends on the [[Prototype]], which is basically Object.create followed by copying the properties of one or more objects over to the new instance. (Object.clone? Object.inherit? I never felt `extend' quite says it all)
Object.prototype.clone(Object parent, Object mixins...) → Object (Object.extend.apply(Object.**create(parent), mixins))
I've been referring to this method as Object.makeRaynos/pd/blob/master/src/pd.js#L120
it's defiantly a useful method, however I don't think "clone", "inherit" or "make" are correct names for it. "clone" is probably the most suitable name, especially if it's on Object.prototype
var foo = Base <| { #constant: 1, // or const @private_stuff: bar, // or private method1() { }, method2() { } get stuff() { } }
Except I'm not sure the @private could be made to work without creating confusing semantics around it, so object literals would still be one step behind.
As far as I understand "@private_stuff" would desugar into a name object only available in scope of the object literal.
var o = { @private_stuff: bar; method1() { } }
var o = (function () { var private_stuff = name.create(); var ret = { method1() { } } ret[private_stuff] = bar; })();
I personally don't find that confusing.
Except I'm not sure the @private could be made to work without creating confusing semantics around it, so object literals would still be one step behind.
As far as I understand "@private_stuff" would desugar into a name object only available in scope of the object literal.
The basic idea of private names is that you don't use names directly, but store them in a variable and use that variable whenever you need to refer to something:
var _name = "MyClass_private_a3cfb"; function MyClass(name) { this[_name] = name; }
The actual private names are very similar to _name above, but they are not strings, they are special objects that are never listed anywhere (one step beyond non-enumerable, if you will).
Thus, the challenge is clear: You have to introduce variables such as _name in a manner that makes them accessible wherever you need them.
var o = { @private_stuff: bar; method1() { } }
var o = (function () { var private_stuff = name.create();
var ret = { method1() { } } ret[private_stuff] = bar; })();I personally don't find that confusing.
The latest ideas are about requiring a private section where you declare private_stuff.
var obj = { private { private_stuff } @private_stuff: bar, method1() { return this. at private_stuff; } }
It does desugar like you mention above. If you use constructor functions without any tricks, then scope is an issue:
var private_stuff = name.create(); // it would be nice to have support for a private section here function MyClass(stuff) { this. at private_stuff = stuff; // same as this[private_stuff] } MyClass.prototype.method1 = function () { return this. at private_stuff; // same as this[private_stuff] }
On Nov 13, 2011, at 5:08 AM, Jake Verbaten <raynos2 at gmail.com> wrote:
Neither of them are fit for standardization. Selfish and Prototype are both incapable of correctly "deep copying" arrays or objects,
Why does it matter that they don't deep copy? Deep copying is a difficult problem that needs to be standardized separately.
It matters because I don't want data pollution across "instances". It's not a hard problem at all, jQuery.extend() has had deep copy for years.
A few more thoughts...
On Nov 13, 2011, at 5:08 AM, Jake Verbaten <raynos2 at gmail.com> wrote:
Neither of them are fit for standardization. Selfish and Prototype are both incapable of correctly "deep copying" arrays or objects,
Why does it matter that they don't deep copy? Deep copying is a difficult problem that needs to be standardized separately. I've personally avoided deep copying for this reason and don't use it anymore.
One can accept that an extend is merely a shallow copy properties by reference, because this (although limited) behavior is easy to understand.
Shallow copy is only good for one level of property lists whose assignment expressions are all primitive, anything that is a reference is going to allow the destination to mutate the source, this is wrong.
I have a version of Object.extend that is a "shallow own merge".
However having a deep copy mechanism that works without obscure edge-cases would be great.
Can you be specific? What obscure edge cases have you previously encountered?
Of course it would be nice if we had the choice of deep vs shallow copy within the API.
Agreed
However having a deep copy mechanism that works without obscure edge-cases would be great.
Can you be specific? What obscure edge cases have you previously encountered?
I don't have a list at hand, last time we talked about what it means to deep copy an arbitary ES-next structure we ran into questions of what it means to deep copy functions, closures and proxies.
Deep copying "data", i.e. anything that can be represented by JSON doesn't have too many edgecases.
It would actually be nice to have an open lists of unsolved edge cases with generic deep copying.
On Nov 13, 2011, at 11:03 AM, Jake Verbaten <raynos2 at gmail.com> wrote:
However having a deep copy mechanism that works without obscure edge-cases would be great.
Can you be specific? What obscure edge cases have you previously encountered?
I don't have a list at hand, last time we talked about what it means to deep copy an arbitary ES-next structure we ran into questions of what it means to deep copy functions, closures and proxies.
Can you point me to existing discussion threads?
Deep copying "data", i.e. anything that can be represented by JSON doesn't have too many edgecases.
It would actually be nice to have an open lists of unsolved edge cases with generic deep copying.
This would definitely be useful.
On Sun, Nov 13, 2011 at 4:51 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
On Nov 13, 2011, at 11:03 AM, Jake Verbaten <raynos2 at gmail.com> wrote:
However having a deep copy mechanism that works without obscure edge-cases
would be great.
Can you be specific? What obscure edge cases have you previously encountered?
I don't have a list at hand, last time we talked about what it means to deep copy an arbitary ES-next structure we ran into questions of what it means to deep copy functions, closures and proxies.
Can you point me to existing discussion threads?
The only thing I can find is esdiscuss/2011-October/017337
This particular thread talked about <| and how to make it work on object references rather then object literals. Since <| returns a new object it would have to "clone" the object reference in some "correct" manner.
On Nov 13, 2011, at 9:24 AM, Jake Verbaten wrote:
On Sun, Nov 13, 2011 at 4:51 PM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Nov 13, 2011, at 11:03 AM, Jake Verbaten <raynos2 at gmail.com> wrote:
However having a deep copy mechanism that works without obscure edge-cases would be great.
Can you be specific? What obscure edge cases have you previously encountered?
I don't have a list at hand, last time we talked about what it means to deep copy an arbitary ES-next structure we ran into questions of what it means to deep copy functions, closures and proxies.
Can you point me to existing discussion threads?
The only thing I can find is esdiscuss/2011-October/017337
Quoting with s/identify/identity/g:
No, generalized object clone is relatively hard. Some other issues:
It isn't just the [[Class]] internal property. It is all the other representational and behavior invariants implied by [[Class]] What if the object is a Proxy instance? How should private name properties be handled. What if there are identity based internal state relationships within the object. What if there are identity bases external relationships about the object (eg, it's used as a key in a WeakMap) etc.
In other language "clone" has generally evolved into a little frame that allows application level control of some or all of these decisions.
I'm not saying that some sort of generalized clone wouldn't be useful. Just that it's not so simple.
Allen
This particular thread talked about <| and how to make it work on object references rather then object literals. Since <| returns a new object it would have to "clone" the object reference in some "correct" manner.
Deep copying "data", i.e. anything that can be represented by JSON doesn't have too many edgecases.
It would actually be nice to have an open lists of unsolved edge cases with generic deep copying.
This would definitely be useful.
See above!
The hard cases include:
- Closures.
- Proxies.
- Private names.
- Internal hidden state.
- Side-table entries mapped to the object's identity.
On Nov 13, 2011, at 9:30 AM, Brendan Eich wrote:
The hard cases include:
- Closures.
- Proxies.
- Private names.
- Internal hidden state.
- Side-table entries mapped to the object's identity.
In the case of objects implemented by C++ or whatever the host implementation language might be, the internal or side-table state may not even be representable in JS, even in strings (do not want raw pointers, or machine addresses however obfuscated, to leak to an attacker).
On Sun, Nov 13, 2011 at 7:42 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Nov 13, 2011, at 5:08 AM, Jake Verbaten <raynos2 at gmail.com> wrote:
Neither of them are fit for standardization. Selfish and Prototype are both incapable of correctly "deep copying" arrays or objects,
Why does it matter that they don't deep copy? Deep copying is a difficult problem that needs to be standardized separately.
It matters because I don't want data pollution across "instances". It's not a hard problem at all, jQuery.extend() has had deep copy for years.
Just a couple of observations:
- There are two different issues here: flatten vs retain [[Prototype]] links and shallow vs deep copy.
- I think the choice should be different for methods and data.
The first order effect of inheritance is to re-use methods; for this purpose shallow copies and re-use by [[Prototype]] links works well. However, if you adopt shallow copies or re-use by [[Prototype]] links, the non-method data is copied by reference, causing write onto derived objects to write onto ancestor object values. This is only rarely the desired outcome.
In Gonzala's selfish and in his gist, I believe the implicit assumption is that developers should adopt a pattern of use: "ancestor class" objects will have only function properties. The |initialize| method will define data properties. In this way the shallow copy and the use of [[Prototype]] links works for the methods and the data is chained through calls to ancestor initialize(). I think this is also implicit in Object.create(): LHS object is expected to be a pile of methods.
This makes a lot of sense and it meshes well with class-thinking. What it says is: adopt a different strategy for methods and for data. For methods, retain the [[Prototype]] links; for data, flatten. The first-order effect is the same as old PrototypeJS solution but the second order effect, data in [[Prototype]] links vs data on the object, is different.
No, I do not have a concrete proposal. But it seems to me that Gonzala's gist extend() should attach the data properties to the instance not the type. It only matters when the inputs have data properties and in that case you probably don't want them on the [[Prototype]] linked object.
jjb
On Sun, Nov 13, 2011 at 9:34 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 13, 2011, at 9:30 AM, Brendan Eich wrote:
The hard cases include:
- Closures.
- Proxies.
- Private names.
- Internal hidden state.
- Side-table entries mapped to the object's identity.
In the case of objects implemented by C++ or whatever the host implementation language might be, the internal or side-table state may not even be representable in JS, even in strings (do not want raw pointers, or machine addresses however obfuscated, to leak to an attacker).
I think we are on the wrong path here. I guess we followed: a standard extend() needs a copy-ish operation; a copyish operation is like cloning; cloning is hard; OMG.
But let's back up. We are looking for one or a few operations such that: var a = op(b,c,d,...); creates a useful result when we use |a| and we are using existing JS libraries for guidance. By definition there are no show stoppers here. We are creating new objects from existing objects using operations available to JS devs, but in standard and recommended way. The only two things can stop us from being successful: irreconcilable differences and inertia.
jjb
On Sun, Nov 13, 2011 at 2:36 AM, Axel Rauschmayer <axel at rauschma.de> wrote:
It depends on what kind of
extends
you mean:
- Inheritance: Even though data being kept in prototypes is rare, it’s usually better to chain prototypes even when ignoring aliasing problems (no redundant properties, instanceof works transitively). Performance degradation should be negligible due to internal optimizations.
- Merging objects: Shallow copy is the way to go.
Perhaps you mean: 'it depends on the use of the object returned by |extends()|'. But I don't understand your categories. Mine:
Object-like: you want to use the returned object directly. I think a dev imagines extend() as a kind of initialization, a merger operation: var a = merge(b,c,d); In this case you do not expect deep copy, you expect shallow, reference copy. If I change d.color, I expect that a.color is changed. Or rather, I would say that a dev would not be surprised at this behavior.
Class-like: you want to use the returned object indirectly, to create other objects via Foo.prototype/new Foo or similar operation. Here we are operating on two levels. In the abstract level we are combining objects to define factory; in the concrete level we are creating objects based on that factory. The data on the inputs to the abstract level are -- at most -- relevant as initial values for the concrete level. You want something like deep copy.
As I mentioned elsewhere, the Object-like merge can be used (with care) on collections of methods then applied to Foo.prototype. That's how we get along now: one operation covers both cases just not always robustly.
jjb
On Nov 13, 2011, at 11:28 AM, John J Barton wrote:
On Sun, Nov 13, 2011 at 9:34 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 13, 2011, at 9:30 AM, Brendan Eich wrote:
The hard cases include:
- Closures.
- Proxies.
- Private names.
- Internal hidden state.
- Side-table entries mapped to the object's identity.
In the case of objects implemented by C++ or whatever the host implementation language might be, the internal or side-table state may not even be representable in JS, even in strings (do not want raw pointers, or machine addresses however obfuscated, to leak to an attacker).
I think we are on the wrong path here. I guess we followed: a standard extend() needs a copy-ish operation; a copyish operation is like cloning; cloning is hard; OMG.
That's not what happened. Some people are happy with shallow copy of properties available to ES5 reflection, or even a for-in loop. Others (Rick?) want deep, at least as an option. Deep could skip any private/internal/etc. properties, for sure. But deep tends to get into trouble because if you don't stay shallow, you run immediately into item 1: Closures. How would those be deeply copied, including their lexical environments?
But let's back up. We are looking for one or a few operations such that: var a = op(b,c,d,...); creates a useful result when we use |a| and we are using existing JS libraries for guidance. By definition there are no show stoppers here.
I agree, other than inability to agree on what to standardize.
We are creating new objects from existing objects using operations available to JS devs, but in standard and recommended way. The only two things can stop us from being successful: irreconcilable differences and inertia.
Ok, but this is all meta-pep-talk. What should the reconcilable standard be?
I think this discussion drifted into slightly diff direction.
What I intended to say was that today all major frameworks use same patter to do subclassing. They all implement different APIs to do the following:
function subclass() {
// init ….
}
subclass.prototype = Object.create(superclass)
Object.defineProperty(subclass.prototype, 'constructor', { value: subclass })
subclass.prototype.method = function() {
//...
}
Object.extend used in my gist is BTW same as backbone's .extend and is just a shortcut of the code above:
var subclass = superclass.extend({ initialize: function() { // init ... }, method: function() { // …. } })
What I'm asking for is a standard function, no matter weather it's Object.extend
or something else that makes it simple to do subclassing. Also lisper in me thinks that Object.extend
method is better than dedicated class syntax cause it keeps language concise. In addition I think that if Object.extend
will turn out to be broken it will be easier (not to say easy) to fix (Object.extend = …
) then fixing class Foo extends Bar
.
-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)
On Friday, 2011-11-11 at 18:38 , Brendan Eich wrote:
Having written all this, I will repeat that I like your selfish work and the exemplar idea
Thanks, that's really encouraging!
On Nov 13, 2011, at 9:42 PM, Irakli Gozalishvili wrote:
I think this discussion drifted into slightly diff direction. What I intended to say was that today all major frameworks use same patter to do subclassing. They all implement different APIs to do the following:
function subclass() { // init …. } subclass.prototype = Object.create(superclass) Object.defineProperty(subclass.prototype, 'constructor', { value: subclass }) subclass.prototype.method = function() { //... }
Yes, this is the heart of the matter. Not freezing, not shallow vs. deep property copying (where deep must deal with closures at least).
Object.extend used in my gist is BTW same as backbone's .extend
Ok, good to know. (Jashkenas, you didn't propose it!)
and is just a shortcut of the code above:
var subclass = superclass.extend({ initialize: function() { // init ... }, method: function() { // …. } })
What I'm asking for is a standard function, no matter weather it's
Object.extend
or something else that makes it simple to do subclassing.
Allen has been trying to make it be a combo of <| and .{. Agree there could be a method, say Object.subclass (what a concept!).
Also lisper in me thinks that
Object.extend
method is better than dedicated class syntax cause it keeps language concise.
Sorry, JS isn't LISP.
In addition I think that if
Object.extend
will turn out to be broken it will be easier (not to say easy) to fix (Object.extend = …
) then fixingclass Foo extends Bar
.
It would be about as hard either way.
On Sun, Nov 13, 2011 at 9:49 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 13, 2011, at 9:42 PM, Irakli Gozalishvili wrote:
I think this discussion drifted into slightly diff direction. What I intended to say was that today all major frameworks use same patter to do subclassing. They all implement different APIs to do the following: function subclass() { // init …. } subclass.prototype = Object.create(superclass) Object.defineProperty(subclass.prototype, 'constructor', { value: subclass }) subclass.prototype.method = function() { //... }
Yes, this is the heart of the matter. Not freezing, not shallow vs. deep property copying (where deep must deal with closures at least).
Sorry I don't understand. Every function which accepts object references and embeds its arguments in [[Prototype]] (either in the return value or the instance created from the return value) faces the copy-ish problem. As I understand it, this is part of (or all of) the reason Allen's setPrototypeOf ( <| ) operator only allows a literal on the right hand side. How can this be avoided?
Object.extend used in my gist is BTW same as backbone's .extend
Ok, good to know. (Jashkenas, you didn't propose it!)
and is just a shortcut of the code above: var subclass = superclass.extend({ initialize: function() { // init ... }, method: function() { // …. } }) What I'm asking for is a standard function, no matter weather it's
Object.extend
or something else that makes it simple to do subclassing.Allen has been trying to make it be a combo of <| and .{. Agree there could be a method, say Object.subclass (what a concept!).
As soon as I call
var a = b.subclass(d, e);
we have to define what happens to |d| and |e|. Isn't that exactly what we have been discussing?
jjb
On Mon, Nov 14, 2011 at 11:48 AM, John J Barton <johnjbarton at johnjbarton.com
wrote:
[snip]
Sorry I don't understand. Every function which accepts object references and embeds its arguments in [[Prototype]] (either in the return value or the instance created from the return value) faces the copy-ish problem.
This is a negligent conflation. Pass by reference is not the same as ensuring that when extending one object with another, the resulting object is not implicitly capable of mutating its source objects (as is the case in Selfish). In Prototype.js's Object.extend(), the behaviour is even worse:
// Object.extend from prototype.js function extend(destination, source) { for (var property in source) destination[property] = source[property]; return destination; }
var defaults = { config: { a: "alpha", b: "beta" } };
function F( options ) { this.settings = extend( defaults, options ); }
var f = new F({ config: { a: "DESTROYED" } });
console.log( defaults.config.a ); // "DESTROYED"
Whoops! Now my defaults
have been changed. If I had other functions
relying on that object, everything becomes chaos. It's important to
remember that Prototype.js has very little developer mindshare these days
and should absolutely not be used as source material for spec reification.
The tests I wrote for Selfish illustrate that it also has this behaviour, but is more deceiving because you think you're getting new "instances".
On Mon, Nov 14, 2011 at 10:42 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Mon, Nov 14, 2011 at 11:48 AM, John J Barton <johnjbarton at johnjbarton.com> wrote:
[snip]
Sorry I don't understand. Every function which accepts object references and embeds its arguments in [[Prototype]] (either in the return value or the instance created from the return value) faces the copy-ish problem.
This is a negligent conflation.
Ouch, that sounds really bad! Except I don't understand what you mean ;-)
Pass by reference is not the same as ensuring that when extending one object with another, the resulting object is not implicitly capable of mutating its source objects (as is the case in Selfish).
I don't understand this sentence. I guess you mean: "The issues of extend() is not related to pass by reference vs pass by value." If so, I disagree.
If extend() only allows object literals as arguments then you don't have to copy, deep or shallow. That's how syntax like Allen's <| avoids the copy problem.
But extend() can't limit its arguments to object literals. So it must deal with copy. Am I wrong?
jjb
On Nov 14, 2011, at 8:48 AM, John J Barton wrote:
On Sun, Nov 13, 2011 at 9:49 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 13, 2011, at 9:42 PM, Irakli Gozalishvili wrote:
I think this discussion drifted into slightly diff direction. What I intended to say was that today all major frameworks use same patter to do subclassing. They all implement different APIs to do the following: function subclass() { // init …. } subclass.prototype = Object.create(superclass) Object.defineProperty(subclass.prototype, 'constructor', { value: subclass }) subclass.prototype.method = function() { //... }
Yes, this is the heart of the matter. Not freezing, not shallow vs. deep property copying (where deep must deal with closures at least).
Sorry I don't understand.
I was agreeing with Irakli that subclass/superclass wiring is primal to classes. How one composes properties from various source objects is a different issue, not directly addressed by class syntax (yet). It is addressed by traitsjs.org and you can use an expression in the superclass position in the class head: class D extends Trait.create(...) {...}.
Every function which accepts object references and embeds its arguments in [[Prototype]] (either in the return value or the instance created from the return value) faces the copy-ish problem.
Irakli was suggesting separating that from setting [[Prototype]] of a new subclass prototype object created at the same time, with constructor wired appropriately. That's what class syntax does at minimum.
You keep bringing in various extend alternatives, but these are separate from classes. Yes, even in the case when the class body is non-empty: in that event the elements define properties on the new class prototype object, which shadow any properties on the base class prototype object. Shadow, not copy.
On Monday, 2011-11-14 at 10:42 , Rick Waldron wrote:
The tests I wrote for Selfish illustrate that it also has this behaviour, but is more deceiving because you think you're getting new "instances".
Hey Rick, I'd be interested to see those tests since it's not supposed to mutate any arguments passed to it, and if that's a case it's a bug and I'd like to fix it.
Irakli, pull request for the tests: Gozala/selfish#1
You keep bringing in various extend alternatives, but these are separate from classes. Yes, even in the case when the class body is non-empty: in that event the elements define properties on the new class prototype object, which shadow any properties on the base class prototype object. Shadow, not copy.
Apologies if I was unclear; I was originally responding to you...
"Let's argue about specifics or we'll get nowhere. Do you think Irakli's selfish.js extend (Gozala/selfish/blob/master/selfish.js) is the way to go, or Prototype's quite different form?
/be"
On Nov 14, 2011, at 12:16 PM, Rick Waldron wrote:
You keep bringing in various extend alternatives, but these are separate from classes. Yes, even in the case when the class body is non-empty: in that event the elements define properties on the new class prototype object, which shadow any properties on the base class prototype object. Shadow, not copy.
Apologies if I was unclear; I was originally responding to you...
"Let's argue about specifics or we'll get nowhere. Do you think Irakli's selfish.js extend (Gozala/selfish/blob/master/selfish.js) is the way to go, or Prototype's quite different form?
I did write that, and the specifics help (it's es-discuss), even when they go into a tangent or separate issue. But Irakli's message about inheritance via prototype wiring got me thinking that the crucial issue for classes (right now) is not composition, but prototypal inheritance.
The super issue was handled separately but works in classes. But apart from <| and some manual subcass-prototype construction and .constructor setting, we don't have a primitive for class inheritance as prototypal delegation -- that is, not one other than minimal classes.
Again I'm not saying the issue John wants to hash out (which is hydra-headed) is "unimportant" or anything like that. Indeed some of us (Alex, Dave, Brendan, Allen) think Traits are excellent. But do they need syntax, or just de-jure codification as built-in library code after de-facto standardization? I think the latter.
Remember, traitsjs.org is rather freeze-gun happy (Dr. Freeze!) and when it was proposed way back in March 2010 to TC39, we talked about less frosty alternatives.
But do they need syntax, or just de-jure codification as built-in library code after de-facto standardization? I think the latter.
Thanks for the clarification; when that discussion comes, I'll revive my points.
On Fri, Nov 11, 2011 at 7:31 PM, Brendan Eich <brendan at mozilla.com> wrote:
Class syntax is wanted to avoid some method calling boilerplate that's more verbose, arguably easier to get wrong, and harder to analyze and optimize. That's it. Hence, "classes as sugar". If you find existing JS sweet enough, you won't want classes. /be
I believe that the biggest advantage of a standard "blessed" way of declaring classes would be that it would become much easier to read each others code.
For example: an experienced Java developer has spent thousands of hours staring at code that follows a standardized top-level pattern, and they have built up the specialized neural machinery that allows them to (for example) grok the top level structure of a class at a glance. (In much the same way that an expert chess player can see in a glance what a less experienced player would have to work out over minutes - or would never see at all).
In current JS practice, almost every program uses a different way of defining the top level structures such as modules, classes, methods and so on - in an important sense every JS program is written in a somewhat different language, and is alien to everyone but it's author.
David Ziegler dz at mudchicken.com
On Mon, Nov 14, 2011 at 11:51 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 14, 2011, at 8:48 AM, John J Barton wrote:
On Sun, Nov 13, 2011 at 9:49 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 13, 2011, at 9:42 PM, Irakli Gozalishvili wrote:
I think this discussion drifted into slightly diff direction. What I intended to say was that today all major frameworks use same patter to do subclassing. They all implement different APIs to do the following: function subclass() { // init …. } subclass.prototype = Object.create(superclass) Object.defineProperty(subclass.prototype, 'constructor', { value: subclass }) subclass.prototype.method = function() { //... }
Yes, this is the heart of the matter. Not freezing, not shallow vs. deep property copying (where deep must deal with closures at least).
Sorry I don't understand.
I was agreeing with Irakli that subclass/superclass wiring is primal to classes.
Irakli proposes a function; you are discussing classes. So perhaps rather than agreeing with Irakli you are drawing a parallel: you like the object created by Irakli's extend() and wish to take that into the "class" discussion?
That's all fine. But I am focusing on the function vs (class) syntax issue. That is where Irakli's "(why) classes" comes from.
How one composes properties from various source objects is a different issue, not directly addressed by class syntax (yet). It is addressed by traitsjs.org and you can use an expression in the superclass position in the class head: class D extends Trait.create(...) {...}.
If we compare a |class| syntax class D extends B adds P; to Irakli's extend, we write: var D = B.extend(P); If both B and P can be expressions, then the forms are equivalent. That is, both forms have to come up with an answer for how the B and P are used in creating D.
Every function which accepts object references and embeds its arguments in [[Prototype]] (either in the return value or the instance created from the return value) faces the copy-ish problem.
Irakli was suggesting separating that from setting [[Prototype]] of a new subclass prototype object created at the same time, with constructor wired appropriately. That's what class syntax does at minimum.
Well he didn't suggest that, but it is a good idea ;-) His extend() could be built out of a 'merge' and a 'subclass', something like:
You keep bringing in various extend alternatives, but these are separate from classes.
Yes, we agree here totally. But from my perspective I would say "You keep bringing up classes, but these are separate from the extend()-like functions".
Yes, even in the case when the class body is non-empty: in that event the elements define properties on the new class prototype object, which shadow any properties on the base class prototype object. Shadow, not copy.
I think some of my previous comments mixed up issues related to [[Prototype]] and issues related to properties. There are some similarities and, to further confuse us, much of the existing practice in this area uses property copies to set [[Prototype]] links via Foo.prototype/new Foo. I'll try to be better.
In the form class D extends B adds P; or var D = B.extend(P); the properties on P shadow the properties on B. But the copy-ish issue fall on B and P individually.
In Java/C++ languages B and P are highly constrained. Consequently the copy-ish issue we discuss here don't apply. If want B and P to be arbitrary expressions, then we have to face how the properties of D relate to the properties of B and P.
I guess Allen's form is: var D = B <| P; where P is constrained to be a literal. I believe it still allows B to have data values, which will be shared between instances of derived objects.
jjb
On Nov 14, 2011, at 2:07 PM, John J Barton wrote:
Irakli proposes a function; you are discussing classes. So perhaps rather than agreeing with Irakli you are drawing a parallel: you like the object created by Irakli's extend() and wish to take that into the "class" discussion?
Irakli and I are trying to separate concerns. We don't need to solve them all together. We don't even need to "solve" them all at once, if the composition concern is not unitary and has many solutions (my "hydra-headed" phrase), none of which is exclusive of any others.
That's all fine. But I am focusing on the function vs (class) syntax issue. That is where Irakli's "(why) classes" comes from.
Sort of. Irakli's post to which I was replying tried to get back to prototypal wiring. He does show a method definition too, but it does not work the way his selfish.js works. Hence (I think, if I'm reading him righ) his desire in the post to which I replied to "get back on track".
The problem is that selfish.js mixes in merging and notably freezing. The last in particular is unnecessary and it doesn't help us make progress. You won't find anyone (probably not even Irakli) arguing to standardize selfish.js as-is, instead of classes.
How one composes properties from various source objects is a different issue, not directly addressed by class syntax (yet). It is addressed by traitsjs.org and you can use an expression in the superclass position in the class head: class D extends Trait.create(...) {...}.
If we compare a |class| syntax class D extends B adds P; to Irakli's extend, we write: var D = B.extend(P); If both B and P can be expressions, then the forms are equivalent.
No, expressions do not guarantee equivalence. Equivalence depends on crucial details about the syntax and semantics of P. We need to specify to be sure "the forms are equivalent". With most of the classes proposals, they aren't. No freezing, for one. Issues of mutating [[Prototype]] vs. copyiing (shallow vs. deep) with [[Prototype]] update abound.
That is, both forms have to come up with an answer for how the B and P are used in creating D.
Here you're suggesting we make an API to underly class syntax, whatever the common semantics.
That may be a good idea, but we need more details to be sure.
No one has objected to APIs for syntactic sugar in general. But the general desire is not a commandment, and in particular, it can be impossible to constrain the semantics and avoid tarpits.
For example, <| has no API that works exactly the same. An Object.beget(lhs, rhs) would observably differ in the case where rhs was not a literal form that constructs a kind of object, in that lhs <| rhs would be an error for any non-literal rhs. The API would have to copy, and now we are back to shallow vs. deep, and if deep, how to copy closures.
So, not to be a broken record: it's crucial to specify the semantics before abstracting and asserting, or even arguing, that we should or must have APIs for all new syntax.
In the form class D extends B adds P; or var D = B.extend(P); the properties on P shadow the properties on B. But the copy-ish issue fall on B and P individually.
Nope, because there's no copy in the class D extend B { <P> } syntax. There is no expression evaluated to compute P as an object, and then reflection on that object to define properties of D's new prototype object. Instead the syntax of P as a class body can be specified entirely without resort to any kind of copy-ish copying.
In Java/C++ languages B and P are highly constrained. Consequently the copy-ish issue we discuss here don't apply. If want B and P to be arbitrary expressions,
That's the "If" I'm pushing back on. I don't necessarily want that as a good in itself (Irakli does). In order to decide if it's wanted, the "it" here needs a spec in the class syntax case. If that spec benefits from the syntax, in ways that are lost with a general expression -- in particular if the general expression form raises hard questions about cloning closures -- then the answer to this "If" is "no".
Irakli's selfish.js does a shallow merge-copy-then-freeze, so it dodges the closure copying issue. But Rick reports unwanted sharing, and I'm pretty sure many people object to the freeze. So what do you want for the copy-ish semantics, if you want the general expression form (that is, the API in addition to or underlying the syntax)?
On Mon, Nov 14, 2011 at 2:27 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Nov 14, 2011, at 2:07 PM, John J Barton wrote:
Irakli proposes a function; you are discussing classes. So perhaps rather than agreeing with Irakli you are drawing a parallel: you like the object created by Irakli's extend() and wish to take that into the "class" discussion?
Irakli and I are trying to separate concerns. We don't need to solve them all together. We don't even need to "solve" them all at once, if the composition concern is not unitary and has many solutions (my "hydra-headed" phrase), none of which is exclusive of any others.
That's all fine. But I am focusing on the function vs (class) syntax issue. That is where Irakli's "(why) classes" comes from.
Sort of. Irakli's post to which I was replying tried to get back to prototypal wiring. He does show a method definition too, but it does not work the way his selfish.js works. Hence (I think, if I'm reading him righ) his desire in the post to which I replied to "get back on track".
The problem is that selfish.js mixes in merging and notably freezing. The last in particular is unnecessary and it doesn't help us make progress. You won't find anyone (probably not even Irakli) arguing to standardize selfish.js as-is, instead of classes.
How one composes properties from various source objects is a different issue, not directly addressed by class syntax (yet). It is addressed by traitsjs.org and you can use an expression in the superclass position in the class head: class D extends Trait.create(...) {...}.
If we compare a |class| syntax class D extends B adds P; to Irakli's extend, we write: var D = B.extend(P); If both B and P can be expressions, then the forms are equivalent.
No, expressions do not guarantee equivalence. Equivalence depends on crucial details about the syntax and semantics of P. We need to specify to be sure "the forms are equivalent". With most of the classes proposals, they aren't. No freezing, for one. Issues of mutating [[Prototype]] vs. copyiing (shallow vs. deep) with [[Prototype]] update abound.
That is, both forms have to come up with an answer for how the B and P are used in creating D.
Here you're suggesting we make an API to underly class syntax, whatever the common semantics.
I'm not suggesting such an API. In fact I am suggesting that such an API is not always possible. If the syntax is class D : B { pList } and requires that B be an identifier and pList list of properties, then the API would have to have restrictions on the arguments that I don't think we have a way to express.
That may be a good idea, but we need more details to be sure.
No one has objected to APIs for syntactic sugar in general. But the general desire is not a commandment, and in particular, it can be impossible to constrain the semantics and avoid tarpits.
For example, <| has no API that works exactly the same. An Object.beget(lhs, rhs) would observably differ in the case where rhs was not a literal form that constructs a kind of object, in that lhs <| rhs would be an error for any non-literal rhs. The API would have to copy, and now we are back to shallow vs. deep, and if deep, how to copy closures.
So, not to be a broken record: it's crucial to specify the semantics before abstracting and asserting, or even arguing, that we should or must have APIs for all new syntax.
I am not arguing what we should or must have APIs for all new syntax. I'm not arguing anything about new syntax. You've already decided not to listen to anyone like me when it comes to new syntax, and I've already agreed that is a good decision.
The only reason I brought up class syntax was illustrate that class syntax can force constraints on the inputs to the inheritance that functions cannot do.
And BTW these constraints may well be the most important advantage of class syntax. These constraints prevent you from pointing the gun at yourself.
In the form class D extends B adds P; or var D = B.extend(P); the properties on P shadow the properties on B. But the copy-ish issue fall on B and P individually.
Nope, because there's no copy in the class D extend B { <P> } syntax. There is no expression evaluated to compute P as an object, and then reflection on that object to define properties of D's new prototype object. Instead the syntax of P as a class body can be specified entirely without resort to any kind of copy-ish copying.
Well I did not write class D extend B { <P> }
specifically because that would imply that the RHS is a literal. If it is a literal, then you don't have to copy, which is exactly my point: the heart of "the problem" is the copy demanded by functions but not by new syntax.
In Java/C++ languages B and P are highly constrained. Consequently the copy-ish issue we discuss here don't apply. If want B and P to be arbitrary expressions,
That's the "If" I'm pushing back on. I don't necessarily want that as a good in itself (Irakli does). In order to decide if it's wanted, the "it" here needs a spec in the class syntax case. If that spec benefits from the syntax, in ways that are lost with a general expression -- in particular if the general expression form raises hard questions about cloning closures -- then the answer to this "If" is "no".
Ok that's a fine answer for the new-syntax case. But it does not help those of us who want functions: we have already decided this question and the answer has to be 'yes'.
Irakli's selfish.js does a shallow merge-copy-then-freeze, so it dodges the closure copying issue.
Irakli makes one choice, a choice Rick dislikes; and Irakli dislikes Rick's choice.
But Rick reports unwanted sharing, and I'm pretty sure many people object to the freeze. So what do you want for the copy-ish semantics, if you want the general expression form (that is, the API in addition to or underlying the syntax)?
I want to survey the existing practice and create a set of tests illustrating the various choices and their trade offs. I don't expect any choice will satisfy everyone.
jjb
On Nov 14, 2011, at 3:16 PM, John J Barton wrote:
I'm not suggesting such an API. In fact I am suggesting that such an API is not always possible. If the syntax is class D : B { pList } and requires that B be an identifier and pList list of properties, then the API would have to have restrictions on the arguments that I don't think we have a way to express.
Right.
BTW, no one proposes that B be an identiifer (which is a kind of expression). Static languages constrain B to be not only an identifier, but one declared previously as a class. We're not doing that. AFAIK all class proposals allow a general expression (probably AssignmenExpression in the grammar, to avoid comma problems) for B.
I am not arguing what we should or must have APIs for all new syntax. I'm not arguing anything about new syntax. You've already decided not to listen to anyone like me when it comes to new syntax, and I've already agreed that is a good decision.
I don't remember it that way, but ok.
The only reason I brought up class syntax was illustrate that class syntax can force constraints on the inputs to the inheritance that functions cannot do.
Good, ok. It sounded like the opposite, honest!
And BTW these constraints may well be the most important advantage of class syntax. These constraints prevent you from pointing the gun at yourself.
Right.
Nope, because there's no copy in the class D extend B { <P> } syntax. There is no expression evaluated to compute P as an object, and then reflection on that object to define properties of D's new prototype object. Instead the syntax of P as a class body can be specified entirely without resort to any kind of copy-ish copying.
Well I did not write class D extend B { <P> } specifically because that would imply that the RHS is a literal.
No, braces don't mean only and ever "literal". Function bodies are not literals, i.e. literal values. They're code bodies. Class bodies could be something else again, and are in Dave's minimial_classes proposal.
But I take your point -- I was trying to be meta not concrete with the <> brackets. Moving along...
If it is a literal, then you don't have to copy, which is exactly my point: the heart of "the problem" is the copy demanded by functions but not by new syntax.
Great, we agree. Why did I think you were arguing the other side? Hrm.
I want to survey the existing practice and create a set of tests illustrating the various choices and their trade offs. I don't expect any choice will satisfy everyone.
A broad survey would be wonderful. Your taxonomy of sharing/copying is good to go. Whether we find one true way or several good approaches, this seeems worth doing. We don't have to standardize anything now (we should not rush) but analyzing the paths can only help (even if some are "bad paths").
I have posted this to the long thread of "Minimalist Classes", but since I have not got any response, I assume it got lost into a long discussion. So I thought I'll give it another try on fresh thread.
I do really liked direction that Jeremy tried to push classes to, but still I don't understand why do we need to introduce new syntax to the language. From what I can tell, lack of classes or special syntax for creating ones, is not a problem. Problem is amount of ceremony one needs to perform inherit or subclass if you like.
Also, I think we don't need new
class
expression to solve actual problems we have, simple function will do the job perfectly here, also it will add zero learning curve! I forked Jeremy's proposal and modified it to ilustrate how existing subclassing problems can be solved without introducing new constructs to the language or adding more verbosity. Please also note that there is nothing new here, lot's of frameworks do this already (hide prototype machinery), but each does it with it's own flavored API which is IMO another problem that standardization should solve. The classes problem is very similar toFunction.prototype.bind
that ES5 solved greatly, why not do the same for classes ?gist.github.com/1355701
In addition I tried to address few other concerns I had with a proposal:
constructor
property as initializer is poor choice raising many questions (what will be prototype of constructor property if it's used in more then one class, if it's frozen etc).Please note, that I intentionally omitted
super
as it's separate problem that is / must be discussed in the separate thread / proposal.-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)
-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)