traits feedback
Thanks for the feedback, John.
I have to admit that at first I was also taken aback by the apparent complexity of the "syntax for efficient traits" strawman. Traits have many knobs, and require a lot of syntax to turn these knobs.
Regardless of whether you'd want nice declarative syntax for traits for the sake of usability, for traits.js in particular, there's another important benefit to having some language support. When using Trait.create to create "tamper-proof" objects whose methods have an unspoofable |this|-binding, the traits.js library creates a method wrapper per method per instance (to bind |this| to the instance). I still need to verify how much overhead this actually creates, but there definitely is overhead, and I don't see any way to avoid it without some support from the language.
Cheers, Tom
2011/10/5 John J Barton <johnjbarton at johnjbarton.com>
On Wed, Oct 5, 2011 at 12:52 AM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:
Thanks for the feedback, John.
I have to admit that at first I was also taken aback by the apparent complexity of the "syntax for efficient traits" strawman. Traits have many knobs, and require a lot of syntax to turn these knobs.
Regardless of whether you'd want nice declarative syntax for traits for the sake of usability, for traits.js in particular, there's another important benefit to having some language support. When using Trait.create to create "tamper-proof" objects whose methods have an unspoofable |this|-binding, the traits.js library creates a method wrapper per method per instance (to bind |this| to the instance). I still need to verify how much overhead this actually creates, but there definitely is overhead, and I don't see any way to avoid it without some support from the language.
Yes I sensed your anguish about this issue. However I believe experimental measurements might surprise you. Lots of JS code involves event handlers passed into DOM calls. I bet lots of JS code already duplicates methods and bind |this|. Only when you have many instances will this overhead matter, and in those cases I think you might be better off without OOP anyway.
jjb
On Wed, Oct 5, 2011 at 10:48 AM, John J Barton <johnjbarton at johnjbarton.com>wrote:
On Wed, Oct 5, 2011 at 12:52 AM, Tom Van Cutsem <tomvc.be at gmail.com>wrote:
Thanks for the feedback, John.
I have to admit that at first I was also taken aback by the apparent complexity of the "syntax for efficient traits" strawman. Traits have many knobs, and require a lot of syntax to turn these knobs.
Regardless of whether you'd want nice declarative syntax for traits for the sake of usability, for traits.js in particular, there's another important benefit to having some language support. When using Trait.create to create "tamper-proof" objects whose methods have an unspoofable |this|-binding, the traits.js library creates a method wrapper per method per instance (to bind |this| to the instance). I still need to verify how much overhead this actually creates, but there definitely is overhead, and I don't see any way to avoid it without some support from the language.
Yes I sensed your anguish about this issue. However I believe experimental measurements might surprise you. Lots of JS code involves event handlers passed into DOM calls. I bet lots of JS code already duplicates methods and bind |this|. Only when you have many instances will this overhead matter, and in those cases I think you might be better off without OOP anyway.
It's been measured: esdiscuss/2010-September/011821
On Wed, Oct 5, 2011 at 8:21 AM, Dean Landolt <dean at deanlandolt.com> wrote:
On Wed, Oct 5, 2011 at 10:48 AM, John J Barton < johnjbarton at johnjbarton.com> wrote:
On Wed, Oct 5, 2011 at 12:52 AM, Tom Van Cutsem <tomvc.be at gmail.com>wrote:
Thanks for the feedback, John.
I have to admit that at first I was also taken aback by the apparent complexity of the "syntax for efficient traits" strawman. Traits have many knobs, and require a lot of syntax to turn these knobs.
Regardless of whether you'd want nice declarative syntax for traits for the sake of usability, for traits.js in particular, there's another important benefit to having some language support. When using Trait.create to create "tamper-proof" objects whose methods have an unspoofable |this|-binding, the traits.js library creates a method wrapper per method per instance (to bind |this| to the instance). I still need to verify how much overhead this actually creates, but there definitely is overhead, and I don't see any way to avoid it without some support from the language.
Yes I sensed your anguish about this issue. However I believe experimental measurements might surprise you. Lots of JS code involves event handlers passed into DOM calls. I bet lots of JS code already duplicates methods and bind |this|. Only when you have many instances will this overhead matter, and in those cases I think you might be better off without OOP anyway.
It's been measured: esdiscuss/2010-September/011821
Well that reference says it was tried once in an undocumented way by one
team focused on other things. Falls short of 'measured' for me.
jjb
On Tue, Oct 4, 2011 at 8:56 PM, John J Barton <johnjbarton at johnjbarton.com>wrote:
In trying to update my JS approach I looked into 'traits'. I'm still on the fence about using them at this stage, but MarkM was asking for feedback of pretty much any kind so here is a little.
I believe I understand traits for the most part just from the info on the Web site: traitsjs.org (but read the examples in the Paper, not the one in the Tutorial). Traits feel like JS to me: a reusable set of methods packaged in an object. The Trait construct add regularity and organization to a wide-spread pattern of JS use, but it feels like a tool you pick up when you need it. The key operations are create and compose, with resolve and override to handle exotic issues.
On the other hand I had the opposite reaction to strawman:syntax_for_efficient_traits That proposal is a completely different language with many new keywords and operators.
I came away deciding to stop using |prototype| + new in favor of Object.create() and to revisit the traits.org library after a bit.
Ok that was a quick experiment. I completely misunderstood Object.create(). I imagined that Object.create(a,b) returned an object with the properties of b and a proto of a. Unfortunately the second argument is some kind of meta object. So to get {p:42} you have to write { p: { value: 42, writable: true, enumerable: true, configurable: true } }. Oy vey.
jjb
Object.create does indeed require propertydescriptors as the second argument. This is the easiest way to send meta-data like read-only.
However it's verbose and the defaults are restrictive. I've written a small library (github.com/Raynos/pd) to make it less verbose, you might find it useful.
On Oct 5, 2011 11:59 PM, "John J Barton" <johnjbarton at johnjbarton.com>
wrote:
On Tue, Oct 4, 2011 at 8:56 PM, John J Barton <johnjbarton at johnjbarton.com>
wrote: > > In trying to ...
Ok that was a quick experiment. I completely misunderstood Object.create(). I imagined that Object.create(a,b) returned an object with the properties of b and a proto of a. Unfortunately the second argument is some kind of meta object. So to get {p:42} you have to write { p: { value: 42, writable: true, enumerable: true, configurable: true } }. Oy vey.
jjb
On Wed, Oct 5, 2011 at 10:36 PM, Jake Verbaten <raynos2 at gmail.com> wrote:
Object.create does indeed require propertydescriptors as the second argument. This is the easiest way to send meta-data like read-only.
However it's verbose and the defaults are restrictive. I've written a small library (github.com/Raynos/pd) to make it less verbose, you might find it useful.
That's what Object.getOwnPropertyDescriptorsstrawman:extended_object_api would
be for. I don't know why it hasn't gotten into Harmony yet.
Juan
On 06/10/11 00:02, Juan Ignacio Dopazo wrote:
On Wed, Oct 5, 2011 at 10:36 PM, Jake Verbaten <raynos2 at gmail.com <mailto:raynos2 at gmail.com>> wrote:
Object.create does indeed require propertydescriptors as the second argument. This is the easiest way to send meta-data like read-only. However it's verbose and the defaults are restrictive. I've written a small library (github.com/Raynos/pd <http://github.com/Raynos/pd>) to make it less verbose, you might find it useful.
That's what Object.getOwnPropertyDescriptors strawman:extended_object_api would be for. I don't know why it hasn't gotten into Harmony yet.
Hm, how exactly would `#getOwnPropertyDescriptors' fix the verbosity of property descriptors when creating new slots in an object? Or were you referring to another issue? If so, I missed it, would you care to explain a little better?
On Wed, Oct 5, 2011 at 8:02 PM, Juan Ignacio Dopazo <dopazo.juan at gmail.com>wrote:
On Wed, Oct 5, 2011 at 10:36 PM, Jake Verbaten <raynos2 at gmail.com> wrote:
Object.create does indeed require propertydescriptors as the second argument. This is the easiest way to send meta-data like read-only.
However it's verbose and the defaults are restrictive. I've written a small library (github.com/Raynos/pd) to make it less verbose, you might find it useful.
That's what Object.getOwnPropertyDescriptorsstrawman:extended_object_api would be for. I don't know why it hasn't gotten into Harmony yet.
Sorry I the property descriptors thing is just for library writers I guess, it's too complicated for app devs.
I think what's missing is Object.extend:
www.prototypejs.org/api/object/extend, api.jquery.com/jQuery.extend, dojotoolkit.org/reference-guide/dojo/extend.html, docs.sencha.com/ext-js/4-0/source/Object2.html#Ext-Object-method-merge, code.google.com/p/fbug/source/browse/branches/firebug1.9/content/firebug/lib/object.js
just some examples in a few minutes. With extend, then we can write
var bar = Object.extend(Object.create(aMethodList), aPropList);
jjb
On Oct 5, 2011, at 8:21 PM, John J Barton wrote:
I think what's missing is Object.extend:
www.prototypejs.org/api/object/extend, api.jquery.com/jQuery.extend, dojotoolkit.org/reference-guide/dojo/extend.html, docs.sencha.com/ext-js/4-0/source/Object2.html#Ext-Object-method-merge, code.google.com/p/fbug/source/browse/branches/firebug1.9/content/firebug/lib/object.js
just some examples in a few minutes. With extend, then we can write
var bar = Object.extend(Object.create(aMethodList), aPropList);
I agree we should specify Object.extend in ES6. It's the API form of the .{ "monocle-mustache" operator but generalized to non-literal RHS.
However, IIRC PrototypeJS uses for-in and does not filter out enumerable inherited properties. It also uses assignment. Neither is good for ES6. We want only "own" properties, including private-name-object-keyed ones. This is good motivation for a built-in.
Given Object.extend, does .{ pay for itself outside of the class pattern use-case? Does a play on dot connote the mutation of the LHS? I think "maybe not" and "no". If we have only Object.extend, we still roll up a popular and common de-facto standard. If we must have an operator, it should take non-literal RHS and contain = in the assignment operator style. So, .= (monocle-nostrils? monocle-thin-man-mustache? ugh).
On Wed, Oct 5, 2011 at 8:44 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Oct 5, 2011, at 8:21 PM, John J Barton wrote:
I think what's missing is Object.extend:
www.prototypejs.org/api/object/extend, api.jquery.com/jQuery.extend, dojotoolkit.org/reference-guide/dojo/extend.html
docs.sencha.com/ext-js/4-0/source/Object2.html#Ext-Object-method-merge
code.google.com/p/fbug/source/browse/branches/firebug1.9/content/firebug/lib/object.js
just some examples in a few minutes. With extend, then we can write
var bar = Object.extend(Object.create(aMethodList), aPropList);
I agree we should specify Object.extend in ES6. It's the API form of the .{ "monocle-mustache" operator but generalized to non-literal RHS.
However, IIRC PrototypeJS uses for-in and does not filter out enumerable inherited properties. It also uses assignment. Neither is good for ES6. We want only "own" properties, including private-name-object-keyed ones. This is good motivation for a built-in.
PrototypeJS (and Firebug) pre-date Object.keys() and .hasOwnProperty(), so their implementation was just what could be done, not what was desired.
Trait.create() parallels Object.create() and I gather that Trait.compose() resembles proposed Object.extend(). I wonder if the traits.js 'parallel-universe' could be applied to Trait.resolve(). In my experience resolve() isn't needed, but academic work on traits suggests otherwise, so it might be a good thing to investigate.
jjb
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
PrototypeJS (and Firebug) pre-date Object.keys() and .hasOwnProperty(),
hasOwnProperty was in ES3 in 1999. PrototypeJS is IIRC 2005-era. Firebug is post-y2k.
so their implementation was just what could be done, not what was desired.
Seems unhistorical :-|.
Trait.create() parallels Object.create() and I gather that Trait.compose() resembles proposed Object.extend(). I wonder if the traits.js 'parallel-universe' could be applied to Trait.resolve(). In my experience resolve() isn't needed, but academic work on traits suggests otherwise, so it might be a good thing to investigate.
Tom Van Cutsem should weigh in.
On Thu, Oct 6, 2011 at 5:44 AM, Brendan Eich <brendan at mozilla.com> wrote:
[...] We want only "own" properties, including private-name-object-keyed ones.
I agree that ".{", being a special form with a literal to the right, should "copy" private-name keyed properties from right to left. However, doing so for a non-literal right hand side seems very dangerous. We should consider this very carefully. I suspect we should reject doing so.
OTOH, both the literal special-form and non-literal library forms should copy non-enumerable own properties from right to left, making for yet another difference from de facto Object.extend. With two significant differences, we should clearly choose another name.
On Wed, Oct 5, 2011 at 10:37 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
PrototypeJS (and Firebug) pre-date Object.keys() and .hasOwnProperty(),
hasOwnProperty was in ES3 in 1999. PrototypeJS is IIRC 2005-era. Firebug is post-y2k.
so their implementation was just what could be done, not what was desired.
Seems unhistorical :-|.
Ok, so what is your theory then?
This code was written by two of the premier JavaScript developers.
If for..in was intended, then Object.extend should also use for...in. The experiment has been done, and for..in is the correct semantics.
If for..in was not intended then what? If developers of this caliber aren't using the new results here, that's really discouraging.
Actually there is a third possibility: it really does not matter. The differences are not significant.
jjb
2011/10/6 John J Barton <johnjbarton at johnjbarton.com>
On Wed, Oct 5, 2011 at 10:37 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
PrototypeJS (and Firebug) pre-date Object.keys() and .hasOwnProperty(),
hasOwnProperty was in ES3 in 1999. PrototypeJS is IIRC 2005-era. Firebug is post-y2k.
so their implementation was just what could be done, not what was desired.
Seems unhistorical :-|.
Ok, so what is your theory then?
This code was written by two of the premier JavaScript developers.
If for..in was intended, then Object.extend should also use for...in. The experiment has been done, and for..in is the correct semantics.
If for..in was not intended then what? If developers of this caliber aren't using the new results here, that's really discouraging.
Actually there is a third possibility: it really does not matter. The differences are not significant.
Actually, I am not sure that having `Object.extend' copy all the enumerable (or all own enumerable) properties by default is a good thing.
First, because that copying would be most expensive, and I don't think most usecases would require the parent properties to be copied as well -- not to say it's easier to have conflicts, even if we're talking just about enumerable here, but then, not copying all the own properties, enumerable or not, makes even less sense, imho.
Second point being is the expensiveness without bringing in a powerful construct. If you need to grab the properties from an object alongside with its parents, you'd rather use a prototype. And it would be easier in this case if JS supported multiple [[Prototype]]s, like Self does. Object.extend then could be regarded just as a means of copying parent-less objects -- semantic-wise.
Third point being that for additional use-cases it's easier to create an abstraction layer that recursively copies the descriptors.
About own/own+enumerable, one has to ask what people use these extensions for. Right now, I believe most people are just copying data from one place to another, such that it's okay to copy only enumerable properties. But now, let's take into account that ES5 introduced property descriptors and getters/setters. Also, let's assume people wanted to use these for copying behaviours as well -- ie.: using the objects passed to Object.extend as mixins. In this case, I believe it makes more sense to copy all own descriptors rather than just the enumerable properties, as some behaviours might depend on a property that is there, but was intentionally marked as non-enumerable to avoid problems -- hash implementations could use that.
I frankly don't think that these new semantic should be fully backwards compatible with ES3-only code, for I believe ES.next will take a while to be adopted. However, I would like hearing about what people's thoughts are on enumerable+own vs all own property copying without to what has been done in pre-ES5 code.
On Oct 6, 2011, at 8:06 AM, John J Barton wrote:
On Wed, Oct 5, 2011 at 10:37 PM, Brendan Eich <brendan at mozilla.com> wrote: On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
PrototypeJS (and Firebug) pre-date Object.keys() and .hasOwnProperty(),
hasOwnProperty was in ES3 in 1999. PrototypeJS is IIRC 2005-era. Firebug is post-y2k.
so their implementation was just what could be done, not what was desired.
Seems unhistorical :-|.
Ok, so what is your theory then?
My point was that your "PrototypeJS and Firebug pre-date .hasOwnProperty()" chronology was wrong. Object.prototype.hasOwnProperty was around since ES3. It was not used in Prototype, but on the other hand, the "never extend Object.prototype" rule was also promulgated.
True, Array.prototype and other built-in prototypes were extended by Prototype. So Object.extend(obj, [1, 2, 3]) would copy inherited methods from Array.prototype to obj, along with properties with keys 0, 1, 2 and values 1, 2, 3 respectively.
Given all this, it's not crystal clear what Object.extend is intended to do. It has definite meaning inherent in its self-hosted implementation, but we may not want to standardize only that. And private name objects are new and unforeseen by Prototype's designers.
This code was written by two of the premier JavaScript developers.
Yes, and we reviewed similar methods a while back from other top JS libraries. See
esdiscuss/2008-July/006709, ejohn.org/files/object-extend.js
You're not making an argument from authority, I hope!
If for..in was intended, then Object.extend should also use for...in. The experiment has been done, and for..in is the correct semantics.
Perhaps, but we have to consider other parts of the language added since then. And with the passage of time and the "Object.prototype is verboten" lesson, we may come to different conclusions from the original authors. Finally, other libraries differ subtly.
If for..in was not intended then what? If developers of this caliber aren't using the new results here, that's really discouraging.
I'm not sure what you mean. Do you mean that someone forgot to use hasOwnProperty to filter out inherited properties in the for-in loop? Cc'ing Andrew, he has spoken about this effectively:
blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542, www.slideshare.net/savetheclocktower/everything-is-permitted-extending-builtins
Actually there is a third possibility: it really does not matter. The differences are not significant.
That could be.
On Thu, Oct 6, 2011 at 10:30 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Oct 6, 2011, at 8:06 AM, John J Barton wrote:
On Wed, Oct 5, 2011 at 10:37 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
PrototypeJS (and Firebug) pre-date Object.keys() and .hasOwnProperty(),
hasOwnProperty was in ES3 in 1999. PrototypeJS is IIRC 2005-era. Firebug is post-y2k.
so their implementation was just what could be done, not what was desired.
Seems unhistorical :-|.
Ok, so what is your theory then?
My point was that your "PrototypeJS and Firebug pre-date .hasOwnProperty()" chronology was wrong. Object.prototype.hasOwnProperty was around since ES3. It was not used in Prototype, but on the other hand, the "never extend Object.prototype" rule was also promulgated.
True, Array.prototype and other built-in prototypes were extended by Prototype. So Object.extend(obj, [1, 2, 3]) would copy inherited methods from Array.prototype to obj, along with properties with keys 0, 1, 2 and values 1, 2, 3 respectively.
Given all this, it's not crystal clear what Object.extend is intended to do. It has definite meaning inherent in its self-hosted implementation, but we may not want to standardize only that. And private name objects are new and unforeseen by Prototype's designers.
This code was written by two of the premier JavaScript developers.
Yes, and we reviewed similar methods a while back from other top JS libraries. See
esdiscuss/2008-July/006709, ejohn.org/files/object-extend.js
You're not making an argument from authority, I hope!
If for..in was intended, then Object.extend should also use for...in. The experiment has been done, and for..in is the correct semantics.
Perhaps, but we have to consider other parts of the language added since then. And with the passage of time and the "Object.prototype is verboten" lesson, we may come to different conclusions from the original authors. Finally, other libraries differ subtly.
If for..in was not intended then what? If developers of this caliber aren't using the new results here, that's really discouraging.
I'm not sure what you mean. Do you mean that someone forgot to use hasOwnProperty to filter out inherited properties in the for-in loop? Cc'ing Andrew, he has spoken about this effectively:
blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542
www.slideshare.net/savetheclocktower/everything-is-permitted-extending-builtins
Actually there is a third possibility: it really does not matter. The differences are not significant.
That could be.
I guess for..in is close to what a developer wants on average. Typically the RHS is an object literal. If the LHS is a plain object, then for..in is fine. If not then for..in gives a result that works most of the time, as it creates an object with all of the properties you expect to be available on the result. It will trade look time for memory, and in rare cases you get a surprise if you imagined that extend() implemented multiple inheritance.
But other choices are similar enough and none will be perfect for all cases.
jjb
On Oct 6, 2011, at 12:30 PM, Brendan Eich wrote:
My point was that your "PrototypeJS and Firebug pre-date .hasOwnProperty()" chronology was wrong. Object.prototype.hasOwnProperty was around since ES3. It was not used in Prototype, but on the other hand, the "never extend Object.prototype" rule was also promulgated.
JJB has it mostly right. When Sam wrote Object.extend (which was originally Object.prototype.extend), it didn't use hasOwnProperty; my guess is that Sam didn't know about it, because most of us were JavaScript amateurs back in the day, including myself. When I joined the project over a year later, I remember discussing this problem and discovering that Safari 2.0 didn't support hasOwnProperty. That, plus the hazard of changing implementation behavior, led us to leave things as they were. If I were writing Prototype's Object.extend from scratch, I'd definitely want it to copy "own" properties only.
Anyway, I'm in favor of standardizing Object.extend in ES6, but in a way that considers only "own" properties (including private names, if so desired). I don't think ES6 should copy the exact behavior of Prototype's (flawed) version of Object.extend. Whether that takes the form of a method named "Object.extend" or a .{ operator… well, that's less important to me, but my vote is for the former.
(Keep in mind that the 80% use-case for this sort of thing is merging default options with user-supplied options, at least in the code I write. That's a simple case that usually involves merging two plain objects without interesting proto values. That's not to say that we should ignore the edge cases; but let's just keep them in the proper perspective, and not let them hold up progress on this front.)
2011/10/6 Brendan Eich <brendan at mozilla.com>
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
Trait.create() parallels Object.create() and I gather that Trait.compose() resembles proposed Object.extend(). I wonder if the traits.js 'parallel-universe' could be applied to Trait.resolve(). In my experience resolve() isn't needed, but academic work on traits suggests otherwise, so it might be a good thing to investigate.
Tom Van Cutsem should weigh in.
A bit late, but nonetheless:
Trait.compose() does not resemble Object.extend(). Trait.override() does. (Trait.compose will flag name clashes in src and dest objects, Trait.override automatically overrides existing properties in dest with those from src).
The parallel to Trait.resolve() would be some operation that can rename/remove properties. For example: var dest = {x:1,y:2};
Object.rename(dest, {x: "a"}) {a:1,y:2} Object.rename(dest, {foo: "a"}) {x:1,y:2} Object.remove(dest, ["x", "y"]) {}
The traits philosophy is that, when merging objects, you want to see name clashes flagged as exceptions (to prevent inadvertent overriding). That's why an additional operation like Trait.resolve() is needed to deliberately avoid those conflicts. I gather than the philosophy of Object.extend is that src always overrides dest properties. That's a different philosophy, but that's fine.
An orthogonal issue is whether Object.extend / Trait.override should be functional or imperative. For traits.js I deliberately chose a functional API. Traits can be used in several different compositions, and you don't want any of those compositions to side-effect your traits. For Object.extend, the common use case may be different.
On Mon, Oct 10, 2011 at 11:59 AM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:
2011/10/6 Brendan Eich <brendan at mozilla.com>
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
The traits philosophy is that, when merging objects, you want to see name clashes flagged as exceptions (to prevent inadvertent overriding). That's why an additional operation like Trait.resolve() is needed to deliberately avoid those conflicts. I gather than the philosophy of Object.extend is that src always overrides dest properties. That's a different philosophy, but that's fine.
Isn't this a case of 'up front pain' vs 'bites me later pain'? src overriding dest is clearly less up front pain since collisions are resolved by rule, but it makes the composition non-commutative, meaning that if you later reverse the order of the composition code can break, esp. dependent objects.
An orthogonal issue is whether Object.extend / Trait.override should be functional or imperative. For traits.js I deliberately chose a functional API. Traits can be used in several different compositions, and you don't want any of those compositions to side-effect your traits. For Object.extend, the common use case may be different.
Sorry to be dense, but can you say a bit more about what you mean here.
jjb
On Mon, Oct 10, 2011 at 4:13 PM, John J Barton <johnjbarton at johnjbarton.com>wrote:
On Mon, Oct 10, 2011 at 11:59 AM, Tom Van Cutsem <tomvc.be at gmail.com>wrote:
2011/10/6 Brendan Eich <brendan at mozilla.com>
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
The traits philosophy is that, when merging objects, you want to see name clashes flagged as exceptions (to prevent inadvertent overriding). That's why an additional operation like Trait.resolve() is needed to deliberately avoid those conflicts. I gather than the philosophy of Object.extend is that src always overrides dest properties. That's a different philosophy, but that's fine.
Isn't this a case of 'up front pain' vs 'bites me later pain'?
Yes, compile-time errors (rather than run-time) are a fundamental feature of traits.
src overriding dest is clearly less up front pain since collisions are resolved by rule, but it makes the composition non-commutative, meaning that if you later reverse the order of the composition code can break, esp. dependent objects.
Precisely. Traits allow composition without an MRO algorithm. Further, they allow remapping fields or methods, allowing strictly more power to composers than an MRO can promise.
An orthogonal issue is whether Object.extend / Trait.override should be
functional or imperative. For traits.js I deliberately chose a functional API. Traits can be used in several different compositions, and you don't want any of those compositions to side-effect your traits. For Object.extend, the common use case may be different.
Sorry to be dense, but can you say a bit more about what you mean here.
I believe what Tom is suggesting is that Trait.override returns newly minted traits rather than mutating, and depending on the usecase, this could be unnecessary and/or too costly.
On Mon, Oct 10, 2011 at 1:45 PM, Dean Landolt <dean at deanlandolt.com> wrote:
On Mon, Oct 10, 2011 at 4:13 PM, John J Barton < johnjbarton at johnjbarton.com> wrote:
On Mon, Oct 10, 2011 at 11:59 AM, Tom Van Cutsem <tomvc.be at gmail.com>wrote:
2011/10/6 Brendan Eich <brendan at mozilla.com>
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
The traits philosophy is that, when merging objects, you want to see name clashes flagged as exceptions (to prevent inadvertent overriding). That's why an additional operation like Trait.resolve() is needed to deliberately avoid those conflicts. I gather than the philosophy of Object.extend is that src always overrides dest properties. That's a different philosophy, but that's fine.
Isn't this a case of 'up front pain' vs 'bites me later pain'?
Yes, compile-time errors (rather than run-time) are a fundamental feature of traits.
Or: create tedious work all of them time rather than doing what you want 99% of the time.
src overriding dest is clearly less up front pain since collisions are resolved by rule, but it makes the composition non-commutative, meaning that if you later reverse the order of the composition code can break, esp. dependent objects.
Precisely. Traits allow composition without an MRO algorithm. Further, they allow remapping fields or methods, allowing strictly more power to composers than an MRO can promise.
Or: require composers to explicitly specify extra details whether it matters or not.
Personally I don't think either approach is intrinsically superior. I don't think any of the library implementations choice to make extend() throw errors on collisions.
jjb
On Mon, Oct 10, 2011 at 5:06 PM, John J Barton <johnjbarton at johnjbarton.com>wrote:
On Mon, Oct 10, 2011 at 1:45 PM, Dean Landolt <dean at deanlandolt.com>wrote:
On Mon, Oct 10, 2011 at 4:13 PM, John J Barton < johnjbarton at johnjbarton.com> wrote:
On Mon, Oct 10, 2011 at 11:59 AM, Tom Van Cutsem <tomvc.be at gmail.com>wrote:
2011/10/6 Brendan Eich <brendan at mozilla.com>
On Oct 5, 2011, at 9:02 PM, John J Barton wrote:
The traits philosophy is that, when merging objects, you want to see name clashes flagged as exceptions (to prevent inadvertent overriding). That's why an additional operation like Trait.resolve() is needed to deliberately avoid those conflicts. I gather than the philosophy of Object.extend is that src always overrides dest properties. That's a different philosophy, but that's fine.
Isn't this a case of 'up front pain' vs 'bites me later pain'?
Yes, compile-time errors (rather than run-time) are a fundamental feature of traits.
Or: create tedious work all of them time rather than doing what you want 99% of the time.
I suspect we have different definitions of tedious. Having my program immediately inform me of a potential conflict, tell me precisely where it is, and give me the tools to easily resolve it strikes me as a lot less tedious than learning of some odd behavior after the fact and rooting through the details of some MRO algorithm to try and tease out what it could be.
src overriding dest is clearly less up front pain since collisions are
resolved by rule, but it makes the composition non-commutative, meaning that if you later reverse the order of the composition code can break, esp. dependent objects.
Precisely. Traits allow composition without an MRO algorithm. Further, they allow remapping fields or methods, allowing strictly more power to composers than an MRO can promise.
Or: require composers to explicitly specify extra details whether it matters or not.
I'd argue it always matters. If there's no conflict, there's no work. If there is a conflict, you'll almost certainly want to know about it. You obviously believe differently, but I suspect you may not be aware of just how little work we're talking about here.
Personally I don't think either approach is intrinsically superior. I don't think any of the library implementations choice to make extend() throw errors on collisions.
If you accept the premise that all method name conflicts are at least problem enough to warrant a second look then I believe traits are clearly superior.
2011/10/10 Dean Landolt <dean at deanlandolt.com>
On Mon, Oct 10, 2011 at 4:13 PM, John J Barton < johnjbarton at johnjbarton.com> wrote:
On Mon, Oct 10, 2011 at 11:59 AM, Tom Van Cutsem <tomvc.be at gmail.com>wrote:
The traits philosophy is that, when merging objects, you want to see name clashes flagged as exceptions (to prevent inadvertent overriding). That's why an additional operation like Trait.resolve() is needed to deliberately avoid those conflicts. I gather than the philosophy of Object.extend is that src always overrides dest properties. That's a different philosophy, but that's fine.
Isn't this a case of 'up front pain' vs 'bites me later pain'?
Yes, compile-time errors (rather than run-time) are a fundamental feature of traits.
Indeed. Or, if compile-time is unattainable, at least a "composition-time" error (i.e. when Trait.compose is called).
src overriding dest is clearly less up front pain since collisions are
resolved by rule, but it makes the composition non-commutative, meaning that if you later reverse the order of the composition code can break, esp. dependent objects.
Precisely. Traits allow composition without an MRO algorithm. Further, they allow remapping fields or methods, allowing strictly more power to composers than an MRO can promise.
An orthogonal issue is whether Object.extend / Trait.override should be
functional or imperative. For traits.js I deliberately chose a functional API. Traits can be used in several different compositions, and you don't want any of those compositions to side-effect your traits. For Object.extend, the common use case may be different.
Sorry to be dense, but can you say a bit more about what you mean here.
I believe what Tom is suggesting is that Trait.override returns newly minted traits rather than mutating, and depending on the usecase, this could be unnecessary and/or too costly.
That is what I meant, thanks for clarifying.
In trying to update my JS approach I looked into 'traits'. I'm still on the fence about using them at this stage, but MarkM was asking for feedback of pretty much any kind so here is a little.
I believe I understand traits for the most part just from the info on the Web site: traitsjs.org (but read the examples in the Paper, not the one in the Tutorial). Traits feel like JS to me: a reusable set of methods packaged in an object. The Trait construct add regularity and organization to a wide-spread pattern of JS use, but it feels like a tool you pick up when you need it. The key operations are create and compose, with resolve and override to handle exotic issues.
On the other hand I had the opposite reaction to strawman:syntax_for_efficient_traits That proposal is a completely different language with many new keywords and operators.
I came away deciding to stop using |prototype| + new in favor of Object.create() and to revisit the traits.org library after a bit.
jjb