taskease.js - Tasks: task #12102, Traits/Mixins

 
 

You are not allowed to post comments on this tracker with your current authentication level.

task #12102: Traits/Mixins

Submitter:  Mike Gerwitz <mikegerwitz>
Submitted:  Sun 10 Jun 2012 03:46:28 AM UTC
   
 
Should Start On:  Mon 20 Jan 2014 05:00:00 AM UTC Should be Finished on:  Thu 01 Oct 2015 04:00:00 AM UTC
Category:  Core Priority:  9 - Immediate
Item Group:  Development Status:  In Progress
Privacy:  Public Assigned to:  mikegerwitz
Percent Complete:  80% Open/Closed:  Open
Planned Release:  0.3.0
Keywords:  traits, mixins

Sat 08 Feb 2014 05:52:00 AM UTC, comment #5: 
[Additional Information]

I'd like to make a correction to my previous comment with regards to how Scala implements traits; I had incorrectly recalled my past experience with the language. Scala does not have any implicit ordering: indeed, you must specify separate mixins using the "with" keyword for each, which resolves all ambiguities and ensures that the supertype is always well-defined.

This will not be necessary with ease.js, but can be done if multiple traits result in ambiguities, since ease.js will not provide conflict resolution (rationale will be in documentation and test cases). For example, if Ta and Tb both attempt to override the same method, ease.js would throw an error; however, if C mixed in Ta and then C' extended C and mixed in Tb, the ambiguity will have been resolved.

Mike Gerwitz <mikegerwitz>
Group administrator
Mon 27 Jan 2014 05:35:18 AM UTC, comment #4: 

The implementation described below has proven to be effective and relatively clean, resulting in functional traits. I now must deal with the finer details, ensuring that it cooperates consistently and cleanly with the remainder of the framework. In particular, it presents interesting approaches for ensuring the proper implementation of overriding virtual concrete methods defined in traits, creating subtypes of classes using traits, ensuring property access of class and trait members are consistent in pre-ES5 implementations, and others.

Each of these issues will be detailed in the commit messages, test cases, and documentation. The trait system for the 0.2.0 release will be much more complete and feature-rich than I had originally hoped for, but may lack certain desirable features such as conflict resolution. This would be intentional---it is much easier to have ease.js blow up now on conflicts than rush into a poorly researched conflict resolution implementation, because the former would be backwards-compatible with existing code written using ease.js. There are also different approaches to consider, and approaches that I will likely reject outright. For example, Scala allows conflicting methods to "win" by having the rightmost trait in a class definition to take precedence, which introduces odd ordering rules that I feel should instead be explicit (rationale will follow when the time comes). Another example is PHP's visibility modification---allowing de-escalating visibility of trait members, which imports public trait methods as protected; this is not desirable, because the class is no longer conforming to the API of some trait T and therefore cannot be considered to be of type T.

Mike Gerwitz <mikegerwitz>
Group administrator
Wed 22 Jan 2014 07:17:09 PM UTC, comment #3: 
[Additional Information]

#13054 sheds some more light on the complexity of implementing traits as the merging of the trait definition objects with the class's definition object. I feel that we may be able to simplify this implementation (at least for the majority of [sane] use cases) by treating traits as a special type of abstract class (which is a route I had originally wanted to explore) and treating mix-ins (both during class definition and instantiation, but we're focusing on the former at the moment) as composition via proxying (that is, in the sense of the `proxy' keyword).

Here's the premise:

  • A trait defines members that can be "mixed into" a class (and maybe other traits directly; tbd); that is, if trait T is mixed into class C, then public/protected T.foo would be accessible as C.foo using the standard visibility rules.
  • A trait has its own private state completely disjoint from all other classes and traits.
    • If C mixes in T, then C does not have access to any private members of T and vice versa.
  • A trait T has access to the protected members of any class C that it mixes into, and vice versa; this permits message passing outside of a public API.
    • But T should only have access to member C.foo if T implements an interface that contains C.foo or extends C.
    • If T1 and T2 (both mixed into C) both contain concrete implementations of a member of the same name, then they are incompatible and the mixin is in error.
  • Assume that T implements some interface I; one of the following must be true:
    • C must implement I and either provide a concrete implementation; or
    • C must implement I and be declared as abstract; or
    • Another trait mixed into C must provide a concrete implementation of each member of I.


Given all of this, we can imagine a composition-based implementation to resemble something like this:

  • Let T be a trait treated similar to an abstract class in that it cannot be instantiated (but only ease.js can instantiate it, because it is not possible to create a concrete Trait by this definition).
    • Let the constructor of T accept the protected visibility object of some class that will eventually mix it in; let this be assigned to T.__c.
    • Let T implement I (or extend some class with abstract members; let it still be considered I); for each abstract member `m' in I, let T.m proxy to private member T.__c.
  • Let class C mix in T. For each public and protected member `m' in T, C.m will be defined such that it proxies to an instance of T.


The consequence of all of this is that we have two-way message passing between traits and classes: C passes messages to T for any public and protected members that it exposes, and T passes messages to C for any abstract members that it does not have concrete implementations to. The consequence of this little dance is profound:

  • T can therefore have access to any public and protected members of C, provided that it implements a common interface.
    • If C does not provide that interface (by implementing a common interface), then that communication is effectively denied and encapsulation is maintained.
  • Let C mix in T0..Tn; any Tx, 0 <= x <= n, can communicate with any other Ty, 0 <= y <= n and x != y, by exchanging messages through C, provided that Tx and Ty implement a common interface.
    • For example, if T1 has abstract method T1.foo and T2 has concrete method T2.foo, then T1.foo would be proxied to C.foo. C.foo would in turn be proxied to T2.foo, because T2.foo has provided a concrete implementation.
  • All traits have their own private scope as a consequence of the composition; no additional work is necessary. In particular, pre-ES5 method patching is unneeded.
  • From all of this, it is clear why two traits mixed into a single class cannot both provide the same concrete member: the ambiguity is unresolvable. However, we can consider ways of providing conflict resolution later on.


I will ponder over my above notes (please excuse any typos or inaccuracies, since I will not be able to edit this message) and see if this is wholly viable; if so, this is the route I will take. The method patching concept is still desirable, but if it can wait for a future release, then that is even more desirable, as it frees up my time to work on this important feature.

Mike Gerwitz <mikegerwitz>
Group administrator
Wed 22 Jan 2014 06:09:09 AM UTC, comment #2: 

See #13054.

I'll decide if I want to immediately implement what is discussed in that task, or if the initial trait implementation will error on private member conflicts just as classes do now. Either way, it will be corrected before the 0.2.0 release, and I want that release to happen well within the month, as I need this functionality.

Mike Gerwitz <mikegerwitz>
Group administrator
Tue 21 Jan 2014 04:03:31 AM UTC, comment #1: 
[Additional Information]

Good news---this feature will provide what I see as a clean implementation of events completely separate from the ease.js core, so I will begin development on traits/mixins immediately.

Mike Gerwitz <mikegerwitz>
Group administrator
Sun 10 Jun 2012 03:46:28 AM UTC, original submission:  

Title says it all. Implementation details will be decided after v0.2.0 release.

This is very important for code reuse and is something I would like to address sooner than later, but there are other things that must be completed first. This is something that will be influenced (time-wise) by its need in projects with which I use ease.js.

Mike Gerwitz <mikegerwitz>
Group administrator

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by mikegerwitz (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

     

    Follow 13 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2015-05-20 mikegerwitz Should be Finished on2014-02-15 2015-10-01
        Percent Complete70% 80%
        Planned Release0.2.0 0.3.0
    2014-02-08 mikegerwitz Percent Complete50% 70%
    2014-01-27 mikegerwitz Percent Complete20% 50%
    2014-01-23 mikegerwitz Percent Complete0% 20%
    2014-01-22 mikegerwitz DependenciesRemoved dependency to task #13054 -
    2014-01-22 mikegerwitz Dependencies- Depends on task #13054
    2014-01-21 mikegerwitz Should Start On2013-01-01 2014-01-20
        Should be Finished on2013-03-01 2014-02-15
        Priority7 - High 9 - Immediate
    2014-01-21 mikegerwitz StatusNone In Progress
        Planned ReleaseNone 0.2.0

    Back to the top

    Powered by Savane 3.13-4448.
    Corresponding source code