I know what you’re going for, and I had this in my initial implementation. The
issue then becomes:
If Trait2 mixes in Trait1, does the init method get replaced? Traits are about
inserting functionality, not about inheritance. Example:
trait Trait1 {
init() {
// init Trait1
}
}
trait Trait2 {
mixin Trait1;
init() {
// init Trait 2
}
}
class Foo {
mixin Trait2;
constructor() {
// do Foo stuff...
}
}
So either init is special and the call order is Trait1.init() Trait2.init()
(while other methods get replaced) or Trait2.init() replaces the Trait1.init().
Also since traits are not classes, they cannot call super. You also are defying
the call stack because trait initializers “just happen” internally. There is no
trail to step through, unlike classes which leaves a trail of breadcrumbs with
super() calls.
This version is actually easy to follow:
trait Trait1 {
initTrait1() {
// init Trait1
}
}
trait Trait2 {
mixin Trait1;
initTrait2() {
this.initTrait1();
// init Trait 2
}
}
class Foo {
mixin Trait2;
constructor() {
this.initTrait2();
// do Foo stuff...
}
}
True, each class has to call the custom init methods. But it still addresses
95% of the problem: Adding in common functionality that doesn’t fit in
classical inheritance. Also since some traits may expect to be mixed in to a
subclass of a certain base, it allows you to call super before the trait
initializers, and that’s something you already have to do in your local
constructor, so initializing traits in the same way makes sense.
The only thing extra beyond copying implementation into a class or another
trait is keeping track of what traits have been mixed in for a “hasTrait”
check. This isn't necessarily required as you can easily check for a method you
want to call, but it does allow you to make sure that the method belongs to
trait and check once for a group of methods.
(This is my ES6 implementation of traits that I’m using now:
https://gist.github.com/lukescott/36453a75c39c539f5c7d
<https://gist.github.com/lukescott/36453a75c39c539f5c7d>)
> On Feb 12, 2015, at 12:47 PM, Andrea Giammarchi <[email protected]>
> wrote:
>
> Luke, forgot one detail: you do not explicitly initialize traits, these are
> initialized if these have an `init` or `initialize` method defined, and
> before the constructor is executed. So, using you example code:
>
> ```js
>
> trait Trait1 {
> initt() {
> this.trait1Data = {what:'ever'};
> }
> }
>
> class Trait1 {
> mixin Trait1;
>
> constructor() {
> console.log(this.trait1Data);
> // {what:'ever'}
> }
> }
>
>
> ```
>
> That makes them really portable, the class is instantly aware of them since
> it used them as mixin, but traits are fully independent.
>
> Do you need a lazy trait? Then you can expose an explicit method to set-it-up
> and invoke it whenever you want in your constructor.
>
> Does any of this make sense? It works already in some case of mine :-)
>
>
>
> On Thu, Feb 12, 2015 at 9:42 PM, Andrea Giammarchi
> <[email protected] <mailto:[email protected]>> wrote:
> Luke, answering in order:
>
> 1. defining order, first come, first serve ... same as everything else on
> daily usage (events, Array of callbacks, etc)
>
> 2. You do not ever pass parameter to them, parameters are constructors
> matters, traits should be independent and unaware of constructors arguments
> otherwise you loose portability.
> A trait should be able to work and enrich any sort of class, not just some
> specific one it has no notion about.
>
> 3. before everything else, since these should be stand-alone behaviors
> unrelated with constructors
>
> All marked as IMO, of course :-)
>
>
> On Thu, Feb 12, 2015 at 9:33 PM, Luke Scott <[email protected]
> <mailto:[email protected]>> wrote:
>
> > On Feb 12, 2015, at 12:11 PM, Domenic Denicola <[email protected]
> > <mailto:[email protected]>> wrote:
> >
> > From: es-discuss [mailto:[email protected]
> > <mailto:[email protected]>] On Behalf Of Benjamin Gruenbaum
> >
> >> We have `Object.assign` that works fantastically for most classic trait
> >> use cases.
> >
> > Well, actually, it works extremely poorly. The old (now dead or deferred)
> > `Object.mixin`, once called `Object.define`, is a better fit. But it still
> > fails to account for a couple things, off the top of my head:
> >
> > - Being able to add custom initialization logic to the class constructor
> > when "mixing in" a trait. You can construct a custom protocol around this,
> > but (a) that means the class you're mixing in to needs to be aware of the
> > protocol; (b) everyone needs to agree on a protocol across the ecosystem.
> > - Pretty much anything to do with private state doesn't work anymore.
>
> The biggest issues of trait initializers are (constructors) are:
>
> - What order do you call them in.
> - How do you pass in parameters into them.
> - They would need to be called by the class constructor. Does this happen at
> the beginning or end?
>
> I would argue that traits should not have initializers. It’s much easier to
> do something like this:
>
> trait Trait1 {
> initTrait1() {
> // this is a regular method
> // init trait here
> }
> }
>
> class Trait1 {
> mixin Trait1;
>
> constructor() {
> // do work here…
> this.initTrait1()
> // do more work here...
> }
> }
>
> This kind of pattern makes initializing traits more intentional and leaves
> out any assumptions of what the correct order would be. And not all traits
> need initializers, so it leaves out a lot of complexity.
>
> _______________________________________________
> es-discuss mailing list
> [email protected] <mailto:[email protected]>
> https://mail.mozilla.org/listinfo/es-discuss
> <https://mail.mozilla.org/listinfo/es-discuss>
>
>
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss