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

Mark S. Miller erights at google.com
Tue Sep 14 09:19:13 PDT 2010


On Tue, Sep 14, 2010 at 6:37 AM, Dmitry A. Soshnikov <
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>wrote:
>
>> On Mon, Sep 13, 2010 at 1:18 PM, Dmitry A. Soshnikov <
>> 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?

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.



>
>
>       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.

>
>>  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.




>
>
>>
>>>
>>> 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.)



>
> 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.

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());



>
> Dmitry.
>



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


More information about the es-discuss mailing list