Because it is not a matter of horizontal reuse.
Why don't you get a warning when you override an inherited method?
Because that is precisely the way things are supposed to work.
The body of a class is not a trait. These methods are not 'equals'.
I still think that design decision is a sensible one with respect to the
rest of PHP.
If traits act like superclasses, why can the class call a private trait
method, by that logic? It shouldn't be able to. You've mixed two paradigms
here and the result is messy.
Traits are for horizontal reuse. If you're justifying this by giving
examples about superclasses, this is the wrong mental model for traits.
Ask anybody "what are traits". The most common answer is "a glorified
copy-and-paste". Do it, you'll see. "Glorified copy-and-paste."
Now ask yourself, if you have class Foo { public $bar; public $bar; } would
that be fine? No. So it's not a copy-and-paste.
But if you copy pasted a private method would you be able to call it? Yes.
So it's copy-and-paste.
So what is it? It's neither and both. I expect a mighty confusion and abuse
of traits because they can't decide what they are in 5.4. Traits should pick
one model and stick to it.
It's very simple:
1) *only* the original name should work for methods defined in a trait
(including metacalls).
2) *only* the new name should work from methods defined in the using
class (including metacalls).
Yes, I know: this is a lot more complicated to implement. *But* if we'll
take shortcuts in user-facing behavior just because it's simpler to
implement, then aliasing should've have never been added at all for 5.4,
until it can be implemented reasonably.
So, what is your proposal?
My proposal is what I said above. Either aliases should be *dropped* as a
feature from traits and the feature has to be rethought, or they have to
work sensibly as outlined in 1) and 2) above.
IMO they do more harm than help in their current form. Sometimes not
implementing a compromise is a feature itself.
If we use aliasing instead of renaming, we have predictable behavior for
all options.
It's not predictable at all. If the class defines "function print" in your
example above, the trait suddenly can't call it's own method anymore!
Metacall or not.
It is calling another method instead, indeed. That's composition, and that's
what traits are designed for.
http://scg.unibe.ch/archive/papers/Berg07aStatefulTraits.pdf
Let me quote that PDF you've linked me to:
"By default, variables are private to the trait that defines them. Because
variables are private, CONFLICTS BETWEEN VARIABLES CANNOT OCCUR when traits
are composed. If, for example, traits T1 and T2 each define a variable x,
then the composition of T1 + T2 does not yield a variable conflict. Variables
are only visible to the trait that defines them"
"By default, instance variables are private to their trait. If the scope of
variables is not broadened at composition time using the variable access
operator, CONFLICTS DO NOT OCCUR AND THE TRAITS DO NOT SHARE STATE."
Emphasis mine.
This is absolutely not what is implemented in 5.4, and this is in part why
we're having this thread now.
Here's the proof:
trait T {
private $foo = 0;
public function set($x) { $this->foo = $x; }
public function get() { echo $this->foo . "<br>"; }
}
trait T2 {
private $foo = 0;
public function set2($x) { $this->foo = $x; }
public function get2() { echo $this->foo . "<br>"; }
}
class C {
use T, T2;
public function classGet () { echo $this->foo . "<br>"; }
}
$x = new C;
$x->set(321);
$x->get(); // Correctly produces "321"
$x->set2(123);
$x->get2(); // Correctly produces "123"
$x->get(); // Incorrect, the paper says it should still be "321", instead
the var is shared, so it's "123"
$x->classGet(); // Incorrect, the paper says this shouldn't work at all,
instead it's "123"
> Assuming *silently* that the user is intentionally colliding state
> variables between traits because he/she wants to merge/split state is
> insanity IMO.
Instead of calling my insane, I would prefer if you make constructive
proposals.
I'm not calling you insane, I'm calling PHP's behavior on trait conflicts
insane, and I definitely stand by this, and the paper you're linking me to
also seems to confirm this. I keep giving you more reasonable behaviors, but
I don't think you're acknowledging them.
I guess, since you are an expert in language design (and an active user of
PHP), you are aware of this behavior:
class T1 {
public function foo() {
$this->prop = 1;
}
}
class T2 extends T1 {
public function bar() {
$this->prop = 2;
}
}
$o = new T2;
$o->foo();
$o->bar();
var_dump($o);
What is your point again?
My point which I've described multiple times at this point is that traits
are not classes, we already have classes in PHP, you know? Imitiating
classes with traits is the wrong mental model for their behavior, and the
paper you're linking me to confirms tht.
There is nothing simple in PHP...
People don't like fatal errors, especially not for things they intend to
just work.
From my example above, the kind of guarantees you expect are just not
something that is part of what PHP is.
Letting conflicts overwrite randomly properties is not "just working". It's
an example of "not working, and it's very hard to debug why."
I don't know who 'they' are, but I am the insane (thanks for that
attribute) person who wrote the RFCs and the implementation. So, either
you accept my answer that static properties > just work, and you fix the
manual by providing a greatly appreciate patch,
or, you open a new thread and propose a change, start an RFC, and all that
jazz.
Once more, I've not called *you* insane, and I hope you can keep the
discussion to the problem at hand. The most important thing here is what is
PHP's problem with the implementation of traits, and it certainly has some.
My goal isn't to turn this into an exchange of insults, but I feel strongly
about the current implementation of traits, and I'm gonna qualify it as I
see is appropriate.
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php