Hacker News new | ask | show | jobs
by agentultra 5697 days ago
I don't understand the vernacular.

One of the noteworthy language additions are Traits – a brand new horizontal code reuse mechanism.

Code re-use mechanism? Horizontal? I didn't realize PHP was so geometric. What an interesting concept.

While traits are technically different from mixins – they are both designed to fill the same gap left by a single-parent inheritance model

So let me try and get this straight: mixins fill a gap left by a single-parent inheritance model and so traits are like mixins because PHP has a single-inheritance model but traits are technically different in some way?

This is getting confusing...

Python, Perl, Ruby, and many languages with multiple inheritance have been using mixins for... well a long time. It's pretty straight forward.

Perl5 got "roles" through Moose and Perl6 has them natively. I think they're explanations are a little more clear. (http://search.cpan.org/dist/Moose/lib/Moose/Manual/Roles.pod)

As the original author of the patch Stefan Marr pointed out – traits are nothing more but a compiler assisted copy and paste.

In true PHP fashion! What an interesting way to think about it.

Conflicts and Aliases

Ok, this is pretty cool. It's an edge case, but there's already a solution. Neat.

Oh... wait a second, I think I've found the meat of it:

Trait method definitions support all modifiers just like regular class methods do – that includes visibility modifiers, final, static and abstract. The latter can be used to define abstract methods expressing requirements for this particular trait

So this is how they're slightly different than regular-old classes? By being able to use all the same keywords as regular old classes?

It seems that the meat of it is at the very end:

Traits only take part in the process of building a class and do not add any new run-time semantics. That means that usual PHP object-oriented functionality (like late static bindings) work as expected when combined with traits.

But this next one is a bit of a gotcha:

Traits can be composed from other traits (the same way classes use traits).

Wait... what? Why would you want to do this? You're just getting back into hierarchies again... and pretty much just working around multiple-inheritance without calling it such.

It's good to see PHP is evolving. Most people still think of PHP4 and shudder (and rightfully so). Evolving means it might be catching up to the superior languages it competes with in the web space. (Note the tongue-in-cheek).

I'm still not convinced, but it's a step forward I am sure for PHP programmers everywhere. Just don't actually use trait-inheritance and you should be fine.

4 comments

I don't understand the vernacular.

It indeed seems like that ;-)

There are four concepts here:

* Multiple inheritance

* Interfaces (abstract types)

* Mixins

* Traits

Multiple inheritance is available in languages like C++ and Python. But multiple inheritance has well known highly problematic side effects that resolve around diamond inheritance and method aliasing. Basically you have a directed graph of parent classes (with diamonds and all) all implementing a foo() method, and then have to pick a method of linearizing that graph to find out which method actually wins if someone calls o.foo(). There are multiple solutions to that, but all are non-intuitive and have caused heaps of trouble. It's just way to hard for a programmer to reason which method will be called in his program.

This is why Java did away with multiple inheritance and just has interfaces. A lot simpler, a lot cleaner, and easy to understand, but sadly lacking in functionality.

The problem is supposed to be fixed by traits and mixins. I think there is no really clean demarcation between the two. My understanding is that traits are more or less mixins, but with the aliasing features and similar stuff you highlight.

The traits are nothing more but a compiler assisted copy and paste is actually a feature, not a bug. This is exactly what you actually want to keep your program understandable: the code/functionality from traits is effectively treated as copied into your class, and there is exactly one "winner" in the race to be the "foo()" method for this class, where the winner is determined by things like aliasing, or order. All the weird situations where parent classes call a method that is overridden in some branch of the inheritance graph just don't occur.

So traits/mixins are IMHO a huge step forward from multiple inheritance or just interfaces. Not that this will help with the mess that is PHP much ...

The best thing i have seen are clojures protocols. They are like interface++. Check it out.
Agreed. Also the conflict resolution is done in an interesting way. If you have 2 traits that have N conflicting methods, you have to rename all N methods in every single class using the traits (even if it doesn't use the methods in question).

Another interesting side effect is that even if you don't use a particular trait, but a conflict appears upstream - boom - "Warning: Trait method hello has not been applied, because there are collisions with other trait methods on TraitsTest in %s on line %d"

If I understand it correctly: You will be warned that a collision appeared, NONE of them will be applied and it's only a warning, so it won't stop the execution until you step on the "missing method" trap. Now that's a creative way to shoot yourself in the foot.

For the sake of readership (assuming that you understand why) this is because traits are compile-time but method calls can be done dynamically at run-time.

So when you say "even if it doesn't use the methods in question" -- well, the compiler has no way to know that.

The difference between mixins and traits is basically that traits happens at compile-time, where mixins are a run time feature. PHP is a bit odd in that it has static object model, where classes are defined as final entities at compile time, then used at run time. Traits hook in at the compile time level. This is unlike most comparable languages (Such as Ruby), where classes are run time constructs.

While odd, and somewhat limiting, there is also some benefit to the static object model that PHP has. It gives some point of certainty in a language that is otherwise very dynamic. In general, it leads to easier-to-reason-about code.

All with the IMHO disclaimer, understood.

> classes are defined as final entities at compile time, then used at run time.

This isn't exactly true. PHP objects can have dynamically assigned members without having magic methods defined. e.g.

    $foo = new stdClass();
    $foo->bar = "baz";
    echo $foo->bar; //"baz"
And now, with "closures", those members can be methods.
Ruby already does this. Modules can include other modules, support public/protected/private members, and have aliasing and conflict resolution (last one included wins)
> (last one included wins) This is why Ruby doesn't do this.

One of the primary purposes of traits is to fix the problem of having to linearize mixins/parents. Aliasing does ease the problems of linearization, but it doesn't solve them all.

If in a mixin-based system like Ruby you include two modules which initially have no conflicting methods, but at some point methods are added such that there is a conflict, the conflict is silently ignored.

Yes, you should pay attention to changes to the mixins you use, but linearization means that whenever a method is added to a mixin, being certain that your module/class has the right method means going through every single class that uses the mixin and looking at all the other mixins that class uses to ensure that they do not conflict. Traits make changing code easier by doing that checking for you. The one disadvantage is that if you want to ignore a conflict you have to say so, but I don't actually think that is a disadvantage: if a Ruby module says "include Foo, Bar" and both Foo and Bar have an explode method, someone reading the code can't tell if the writer of the code was aware of the two explode methods and chose to use the Bar method, or if they were unaware of the conflict and might have either wanted no explode method or the Foo explode method.

While you're right about that point, there is a reason why conflicts are allowed: inheritance chain ([1] for demo) which provides you a VERY powerful tool for chain-combining traits.

[1] http://www.dcmanges.com/blog/20