"Syntax for Efficient Traits" is now ready for discussion (was: Classes as Sugar is...)

Dmitry A. Soshnikov dmitry.soshnikov at gmail.com
Tue Sep 14 12:09:34 PDT 2010


  On 14.09.2010 20:19, Mark S. Miller wrote:
> On Tue, Sep 14, 2010 at 6:37 AM, Dmitry A. Soshnikov 
> <dmitry.soshnikov at gmail.com <mailto:dmitry.soshnikov at gmail.com>> wrote:
>
>     On 14.09.2010 1:03, Mark S. Miller wrote:
>>     On Mon, Sep 13, 2010 at 2:02 PM, Mark S. Miller
>>     <erights at google.com <mailto:erights at google.com>> wrote:
>>
>>         On Mon, Sep 13, 2010 at 1:18 PM, Dmitry A. Soshnikov
>>         <dmitry.soshnikov at gmail.com
>>         <mailto:dmitry.soshnikov at gmail.com>> wrote:
>>
>>
>>             I didn't finished a detailed reading yet, but from the
>>             brief scanning, syntactically, I think /=>/ and /trait
>>             class/ are not needed.
>>
>>
>>         * I used "trait class" rather than "trait" for two reasons:
>>
>>             1) Syntactic ambiguity fear. "trait" is not one of the
>>         identifiers reserved by ES5 or ES5/strict, and so I am not
>>         proposing that it be an ES-Harmony keyword.
>>
>
>     This keyword would look interesting though. Traits/mixins concept
>     is a weighty thing and if to reproduce it, then in a good way --
>     easy to use/write and syntactically minimalistic. By the way,
>     JFTR, PHP also now providing traits
>     (http://wiki.php.net/rfc/horizontalreuse) with using this /trait
>     TraitName {}/ syntax; Scala too has such syntax -- i.e. without
>     (trait class, class trait) referencing to parser issues.
>
>
> JFTR?

Sorry -- "just for the record", as already Dave has mentioned.
>
> PHP and Scala can tolerate a much higher migration tax than JavaScript 
> because of the differences in how they're deployed. I hesitate to 
> propose any new keywords beyond those reserved by ES5/strict. All the 
> proposals on 
> <http://wiki.ecmascript.org/doku.php?id=harmony:proposals> and the 
> current traits strawman are upwards compatible from ES5/strict, 
> allowing a conforming implementation to provide them with no further 
> opt-in than "use strict". Not all the strawman have this virtue, some 
> with good reason. But to ease incremental prototyping and adoption, we 
> should preserve this virtue when we can.
>

Yeah, yeah, I know, "backward compats". Forget that they are rule.

>
>
>>             2) More importantly, the object binds to the name it
>>         declares is not a trait but a function for making traits.
>>
>>
>>     Oops. Should be "the object bound to ..."
>>
>>
>>         * I introduced "=>" to disambiguate whether the "{" following
>>         the (trait) class head begins a block or a literal.
>>
>
>     Yeah, but maybe somehow it (ambiguous) may be avoided.
>
>
> Somehow sure. Suggestions appreciated.
>
>
>
>>
>>             /trait class EnumerableTrait() => {/
>>
>>             instead better:
>>
>>             trait Enumerable {
>>
>>
>>         Enumerable is a function for making traits. Your syntax
>>         suggests that Enumerable is itself a trait.
>>
>
>     Hm, "function for making trait", "trait itself". Too many things.
>     It may bring complexity. Yes, i thought (and see in other
>     languages, the same Ruby) that Enumerable is a trait itself, and
>     not a special (non-needed to me) function. A user needed just a
>     trait, not a function + trait.
>
>
> Please check out <http://traitsjs.org/>. The notion of traits there 
> and in this strawman is the "stateful traits" approach that Tom first 
> explained at 
> <http://prog.vub.ac.be/Publications/2009/vub-prog-tr-09-04.pdf>. Until 
> I saw this paper, I had written traits off as a dead end.

It seems something interesting there, thanks, I'll take a look on the 
paper (as well as on all examples on the site). However, I'm aware that 
concept of a "trait" in contrast of a "mixin" has no state to avoid 
conflicts.

>>
>>         Without the "=>", how do we know that the "{" begins a
>>         literal rather than block? (See the second TraitBody production).
>>
>>
>>             /class Interval(min, max) => {/
>>
>>             instead better:
>>
>>             class Interval(min, max) {
>>
>>             or even (with initialize method)
>>
>>             class Interval {
>>               function initialize(min, max) {}
>>             }
>>
>>             Superfluous symbols are not needed.
>>
>>
>>         Initialize methods are superfluous as well. Also, they
>>         introduce namespace confusion and re-initialization hazards.
>>
>
>     Yep, but how then you propose to initialize an instance? I mean,
>     not a state, but the initial code. In the class body?
>
> In the "Program" portion of the second TraitBody production. This can 
> declare new variables that are captured by the following TraitLiteral. 
> The lexical variables captured by each TraitLiteral is the state of 
> that trait. The union of the states of the traits that a class 
> composes together is the state of an instance.
>
With a state it's OK. But usually, an initialization process may include 
internal methods calling (which of course also just create a state 
properties, however, there is a difference -- conveniently to call three 
methods, which will set 10 properties, or to define these 10 properties 
manually in the body of a class).

>
>
>>
>>             And still, don't forget about the (vertical) inheritance,
>>             i.e. class Foo may /extends/ Bar. If there will be no
>>             (delegation based) inheritance, such classes won't be
>>             needed, because they will be just casual
>>             pattern-factories. In addition, mixins/traits, being a
>>             horizontal inheritance (the augmentation of the vertical
>>             code reuse) are also good to have as delegation based. I
>>             think that you (and Tom) used statically augmented (a
>>             method per instance) object not because you like it
>>             ideologically, but because it's not possible to implement
>>             it cross-browser in nowadays.
>>
>>
>>         I'm not sure what you mean. Could you please expand and
>>         clarify the question? Thanks.
>>
>
>     I mentioned "classes-as-sugar" proposal, where a class cannot
>     inherit (making a vertical inheritance chain).
>
>     A -> B -> C
>
>     In addition, if any class may mixin several traits/mixins, we have
>     a horizontal inheritance:
>
>     A -> B -> C
>     |      |
>     |      |----------- m1 -> m3
>     |
>     |--m1 -> m2
>
>     i.e. at every link, first the whole chain of mixins is considered
>     and only after that we go to the next (vertical) link. Sure, the
>     normalized inheritance chain for resolution is linear:
>
>     A -> m1 -> m2 -> B -> m1 -> m3 -> C
>
>     I.e. an instance always inherits methods/properties from mixins or
>     classes, but not has own (as in your implementation of classes and
>     traits). The thing is not only in optimizing VM, but in dynamics
>     of a language. I.e. if we modify somehow methods in the class or
>     trait, all instances via delegation will have a new version.
>
>     That what Ruby uses:
>
>     module MyTrait
>       def my_method
>         print(@items)
>       end
>     end
>
>     class MyCollection
>
>       # mixin MyTrait to the class
>       include MyTrait
>
>       # and also some built-in mixin
>       include Enumerable
>
>       def initialize(data)
>         @data = data
>       end
>
>       # method required by the
>       # Enumerable trait/mixin
>       def each
>         @data.each { |i| yield(i) }
>       end
>
>     end
>
>     # an intance
>     things = MyCollection.new(['x','yz','defgh','ij','klmno'])
>
>     thigns.my_method # from MyTrait
>
>     # available from Enumerable trait
>     print(things.min) #=> "defgh"
>     print(things.max) #=> "yz"
>
>     # aka Array#map in JS
>     print(things.collect { |i| i.upcase }) #=> ["X", "YZ", "DEFGH",
>     "IJ", "KLMNO"]
>
>     # modules/traits are open
>     module Enumerable
>       def new_method
>         print(@items, " from new method")
>       end
>     end
>
>     # a new method is available
>     # for the instance via delegation
>     things.new_method #=> ['x','yz','defgh','ij','klmno'] from new method
>
>
> I'm still not sure I understand you correctly. But if I do, Override 
> is your vertical composition and Compose is your horizontal 
> composition. Via Object.create, one can also mix traits with vertical 
> composition using the JavaScript prototype chain. But then unbound 
> inherited methods become observable, leading to all the normal 
> confusions. By using only Override for vertical composition, only 
> bound methods are observable. (This is like the old "binding on 
> extraction" property of old ES4 classes.)
>
Yes, regarding the end state of an object (via override/composition) you 
understand it correctly. But I meant exactly delegation based 
inheritance and asked -- why do you have (propose) a scheme where an 
object will have /own/ properties but not inherited? In Ruby as I 
showed, if we change Enumerable module (read Enumerable trait/mixin) by 
adding a new method "new_method" then the instance "things" of the class 
"MyCollection" automatically via delegation has access to the new method.

And regarding classes, I think it's not acceptable not to have 
inheritance and to create also own properties. Even if will be a good VM 
optimization, the dynamics of the languages is lost -- if we add a new 
method to a class -- will all instances see this new method? That 
exactly I'm asking.


>
>     P.S.: Why am I asking? -- just see that you everywhere use
>     static/const and frozen things. Want to know, do you propose JS to
>     be a static language without opened classes/protos/traits/etc,
>     "monkey-patching", etc.?
>
>
> Not at all. As previously discussed, JavaScript will lose none of its 
> support for monkey patching, low integrity patterns, unfrozen objects, 
> etc. None of these proposals take any of those away.
>
Just to clarify and not to confuse terminology/meanings, do I understand 
correctly: "low integrity " -- non-|this|-bounded inherited functions, 
and "high integrity" -- vice-versa -- statically bound |this| and own 
properties? Thanks.

> Further, trait classes can be instantiated with Object.create, so 
> traits even enhance the expressiveness of low integrity patterns and 
> vertical composition using prototypes if that's what you wish. 
> Adapting a previous example:
>
>     function Point(x, y) {
>       this.x = x;
>       this.y = y;
>     }
>     Point.prototype = Object.create(
>       SuperPoint.prototype,
>       StatelessPointTrait());
>

Yeah, I see. But again, instantiation of a trait creates own properties 
of an instance, but not just adds additional link for delegation.

By the way, the phrase "a trait instantiate" in general case contradicts 
to traits concept. Because in many (all?) implementations and some 
general definitions of traits -- traits in contrast with classes cannot 
instantiate objects. They are just additional modules (to add small 
traits for the instance).

>
>     Dmitry.
>
>
>
>
> -- 
>     Cheers,
>     --MarkM

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20100914/d61f5751/attachment-0001.html>


More information about the es-discuss mailing list