Read ahead

PHP 5.3 is going to be fun. I personally didn’t really follow the news on this one. I heard of lambda/closures and that pleased me. I just always hated defining functions for callbacks used only once. Not that it takes time, but I always felt forced to create a ridiculously long name for it to avoid any possible conflicts. Those times will be over… at least from the moment it will be deployed enough to justify forcing 5.3 as a requirement.

One fact that I find really interesting about the implementation is that it added functors to PHP. Basically, you can now create function that allow callbacks as parameters, that callback being an object or just a function without too much trouble. Just like STL does.

<php

class Foo {
	function __invoke() {
		return 42;
	}
}

$f = new Foo;

echo $f(); // prints 42

?>

However, this is just a side effect.

One concept that was new to me was traits in Sebastian’s presentation. This one won’t be included in 5.3, so it will be for 5.4 or 6.0. The goal of these is to share code between classes without using inheritance, hence avoiding multiple inheritance. While I can see it’s a cleaner way to do it, I really don’t find the argument compelling.

The implementation details for these are not fixed yet, so don’t worry so much about the syntax.

The main argument against multiple inheritance is the diamond effect. Consider an interface A which is implemented by B and C. If a class D inherits both B and C, problems occur because of an ambiguity. There is no way which parent method call should be used. Typically, the way this is resolved is by redefining that method from D class and choosing either implementation or both. Human decision is required.

I’m personally not really for multiple multiple inheritance, nor against. I just don’t care so much about the issue. I don’t mind if a language allows you to shoot yourself in the foot. You should be smart enough to make the right decisions, or be able to learn from your mistakes. I’m mostly against people being against it for that kind of argument. There are solutions for it and it’s not that complex to resolve. The fact that multiple languages out there have different ways of handling it is no issue.

I think the problems you can have with a less complex structure than that diamond are worst. The diamond problem really occurs because both classes inherit from the same interface most of the time. It means both classes’ implementation really have the same purpose. If you take of that common interface, the conflicts that occur are purely name conflicts, based on a lack of namespace. While it’s likely that they do something similar, it may not be the case. If A it taken off the picture and some function foo() in B returns a collection and foo() in C returns an integer, the problem you have is a whole lot worst. You can no longer just define a wrapper that does either or both implementations.

Traits fully allow that, and they allow it in a much more subtle way. I’m not saying traits are bad. I think it’s a really nice way to share code between different classes, but the argument against multiple inheritance just does not fit.

<?php

trait B {
	function foo() {
		return array( 1, 2, 3 );
	}

	function bar() {
		return 2;
	}
}

trait C {
	function foo() {
		return 5;
	}

	function baz() {
		return 4;
	}
}

class D {
	use B, C;
}

?>

In this kind of situation, you get the exact same conflict. The same function name exists in the traits. Which one do you use? In fact, you can get a conflict with an even simpler model:

<?php
trait B {
	function foo() {
		return 5;
	}

	function bar() {
		return 2;
	}
}

class D {
	use B;

	function foo() {
		return array( 1, 2, 3 );
	}
}
?>

This is an other kind of conflict. Logic would say that the class has precedence, but because the traits are really a compile time hack, it’s not that obvious. The solution to both kinds of conflicts is to have a syntax to alias the functions as they get copied to the new class. It works, but you can do that with multiple inheritance too by just redefining the functions and calling the appropriate parent.

Of course, those kinds of conflicts are not likely to occur at design time. When you make your design, everything is beautiful and works. Things break when they evolve. If you have control over all the code, it’s really easy to manage, because you have unit tests, don’t you? Well, as part of a language, these things will be used by frameworks. If the framework decides an additional method is required in the traits, it may break code all around. Oops. Not quite better than multiple inheritance.

I still think traits are a good thing, but you need the right argument for it. The reason why they make more sense is because they allow you to keep a coherent whole. Multiple inheritance is used to avoid copying code in multiple classes. It’s a matter of laziness. The relationships between the concepts don’t really have anything to do with inheritance. Traits allow to represent that relationship for what it really is. But don’t tell me it’s a perfect solution.

From what I understood, there was also some open questions about how to handle states from traits. Basically, the trait is stateless, but if it can’t access the object’s data, there is no way you can do anything more useful than you would from a static function. One of the current proposals seemed to be to allow the trait to define abstract functions from the trait itself, which would mean that the trait would effectively be an implementation and an interface. An abstract trait just seems confusing.

<?php

trait X {
	function foo() {
		return $this->bar() * 2;
	}

	abstract function bar();
}

?>

I think it could be made a lot more cleaner, and allow more reuse, if the trait instead had requirements:

<?php

interface Barable {
	function bar();
}

trait X requires Barable {
	function foo() {
		return $this->bar() * 2;
	}
}

class D implements Barable {
	use X;

	function bar() {
		return 5;
	}
}

?>

It might seem small, but it could avoid duplicating interfaces in traits, or needing to have traits to implement interfaces.

I think it’s really great to see PHP still evolving, and with the transition to PHP 5 getting faster every month, it means we will be truly able to use these features soon.

Leave a Reply

Your email address will not be published. Required fields are marked *