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

Reply via email to