Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stefan Marr
Dear Stan:

On 01 Aug 2012, at 01:23, Stan Vass wrote:

 
 1. Name collisions between a trait method and a class method using the trait 
 go unreported, the class silently shadowing the trait method:
 
 
 trait T {
function foo() { $this-bar; }
function bar() { echo 'trait'; }
 }
 
 class C {
use T;
function bar() { echo 'class'; }
 }
 
 $c = new C;
 $c-foo(); // class
 
 Proposed behavior: Fatal error on collision, unless the method is imported 
 with a unique name using the { ... as ... } syntax.

This happens to be 'by design'.

The methods in the class have *always* higher precedence.
This is the same 'overriding' behavior used for inheritance.
From my personal perspective, changing this would lead to a major inconsistency 
with how subclassing works.

If you disagree, please open an explicit thread for this problem and report it 
in the bug tracker.



 
 2. Using as syntax when importing a trait does NOT rename a method, but 
 creates an alias CLONE, the original method still callable.
 
 
 trait T {
function bar() { echo 'trait'; }
 }
 
 class C {
use T { bar as foo; }
 }
 
 $c = new C;
 $c-bar(); // trait
 
 Proposed behavior: the original name should be only accessible within the 
 trait and its methods, not from the class methods or by calling the class 
 instance's methods from outside.

Again, this happens to be 'by design'.
PHP is a rather dynamic language, and we happen to have things like $c-$foo();
where $foo is a string. Renaming is technically not reasonable, and would also 
lead to major confusion when metaprogramming is used.

Bye the way, this is documented: 
https://wiki.php.net/rfc/horizontalreuse#renaming



 
 3. Properties silently collide in traits and classes.
 
 
 trait T1 {
private $foo;
 
 trait T2 {
private $foo;
 }
 
 class C { use T1, T2; } // No error.
 
 Proposed behavior: An error is produced only when the properties differ in 
 visibility or a default value, which is clearly insufficient to determine 
 they're used for the same purpose, and hold the same data. Instead they 
 should use the same logic as method conflicts: fatal error on name collision. 
 Alternatively, each trait property whould be accessible within the trait that 
 defines it, not from other traits used in the same class, or the class itself.

Please read 
https://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate carefully.

Again, this is intended behavior.
If you want to be notified of such collisions, please use E_STRICT.



 
 4. The documentation says static propeties can't be defined by traits. Yet 
 they can.
 
 
 I don't know what's the bug here: a doc bug, or a code bug. For consistency, 
 static properties should work, if instance properties work. Nothing is gained 
 supporting it half-way.

Could you please point me *exactly* to the paragraph where we have something 
written about static properties? I do not see that I wrote anything special 
about static properties in the RFC. And I do not find it in the docs either.

static properties should work like normal properties.


Best regards
Stefan


-- 
Stefan Marr
Software Languages Lab
Vrije Universiteit Brussel
Pleinlaan 2 / B-1050 Brussels / Belgium
http://soft.vub.ac.be/~smarr
Phone: +32 2 629 2974
Fax:   +32 2 629 3525


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stan Vass

The methods in the class have *always* higher precedence.
This is the same 'overriding' behavior used for inheritance.
From my personal perspective, changing this would lead to a major 
inconsistency with how subclassing works.


Hi, I've subsequently read your RFC carefully and saw some of those items 
are by design. However while they're by design, the end result seems rather 
confusing I should say.


While I'm glad the RFC was shooting for consistency in general, traits are 
not subclassing. Therefore aligning the behavior of features for vertical 
reuse with features for horizontal reuse seems somewhat arbtirary to me. The 
use cases for overriding methods like this seem sketchy at best. What is the 
use case, and how are those use cases more important than the confusion 
caused by allowig this?


2. Using as syntax when importing a trait does NOT rename a method, but 
creates an alias CLONE, the original method still callable.
 Proposed behavior: the original name should be only accessible within 
the trait and its methods, not from the class methods or by calling the 
class instance's methods from outside.



Again, this happens to be 'by design'.
PHP is a rather dynamic language, and we happen to have things like 
$c-$foo();
where $foo is a string. Renaming is technically not reasonable, and would 
also lead to major confusion when metaprogramming is used.


Can you please explain how are function names by variable at all related to 
this? $object-foo() and $object-$foo() aren't even related in any way 
semantically. If a conflict exists while grafting a trait, the original 
method will be overshadowed silently and the method will be only accessible 
by the alias. The entire idea of aliasing is to avoid a conflict with an 
existing method of the same name. While if there's no collision, it's 
accessible by both. So in practice it works by chance. How is working by 
chance in any way aiding meta programming?



3. Properties silently collide in traits and classes.
Please read 
https://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate 
carefully.


Again, this is intended behavior.
If you want to be notified of such collisions, please use E_STRICT.


I've read them carefully now, but there's no solid reason given for this 
behavior, or why the proper behavior only exists in E_STRICT.
I can't really see state is a complex topic as a reason why trait property 
collisions are handles inconsistently with method collisions.


4. The documentation says static propeties can't be defined by traits. 
Yet they can.


I don't know what's the bug here: a doc bug, or a code bug. For 
consistency, static properties should work, if instance properties work. 
Nothing is gained supporting it half-way.


Could you please point me *exactly* to the paragraph where we have 
something written about static properties? I do not see that I wrote 
anything special about static properties in the RFC. And I do not find it 
in the docs either. static properties should work like normal properties.


Sure. From the manual page for traits: Static variables can be referred to 
in trait methods, but cannot be defined by the trait. This paragraph is 
then following by a code example using function-static variables as what 
seems like an example for a replacement for static trait properties, 
strongly suggesting the former aren't supported. Yet they are.


The RFC seems to also give examples of method-level static variables, and I 
honestly can't see how this is related to traits or trait/class-level 
members at all.


Stan 



--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stan Vass
Could you please point me *exactly* to the paragraph where we have 
something written about static properties? I do not see that I wrote 
anything special about static properties in the RFC. And I do not find it 
in the docs either. static properties should work like normal properties.


I'd like to add something to my answer to this question: while I've added a 
doc bug for this quote I gave, people handling the documentation *don't want 
to fix it*, because they think static members *should notwork* in traits, 
this is how they read the RFC for some reson. Thus a clear sign people are 
confused about this behavior. It has to be clarified.


Stan. 



--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stefan Marr
Hi Stan:

On 01 Aug 2012, at 13:54, Stan Vass wrote:

 The methods in the class have *always* higher precedence.
 This is the same 'overriding' behavior used for inheritance.
 From my personal perspective, changing this would lead to a major 
 inconsistency with how subclassing works.
 
 Hi, I've subsequently read your RFC carefully and saw some of those items are 
 by design. However while they're by design, the end result seems rather 
 confusing I should say.
 
 While I'm glad the RFC was shooting for consistency in general, traits are 
 not subclassing. Therefore aligning the behavior of features for vertical 
 reuse with features for horizontal reuse seems somewhat arbtirary to me.

Horizontal reuse is 'fit into' the hierarchy of things:

  1. super class
  2. horizontal reuse
  3. actual class

From that follows that traits override the super class's methods.
And, conflict resolution is only done between traits, not between a class and 
its traits.
The class body is a definite thing. Why would you purposefully add something to 
a class body that conflicts with a trait?
The class needs to know exactly what the traits are doing. There is no 
black-box reuse.


 The use cases for overriding methods like this seem sketchy at best. What is 
 the use case, and how are those use cases more important than the confusion 
 caused by allowig this?

I don't see your problem. Can you please elaborate with an example (other than 
your initial one?

The use case for overriding is typically that you got a conflict that you need 
to resolve by making a call to both conflicting methods.
Consider two traits that happen to provide a log() function, and in the 
resulting class you need to call both in all places log() has been called 
before.

Since the user of these traits is the only place where such semantic is 
sensibly defined, the method in the class body is overriding the traits 
methods, and can access aliases you introduced for the traits' methods.


 2. Using as syntax when importing a trait does NOT rename a method, but 
 creates an alias CLONE, the original method still callable.
 Proposed behavior: the original name should be only accessible within the 
 trait and its methods, not from the class methods or by calling the class 
 instance's methods from outside.
 
 Again, this happens to be 'by design'.
 PHP is a rather dynamic language, and we happen to have things like 
 $c-$foo();
 where $foo is a string. Renaming is technically not reasonable, and would 
 also lead to major confusion when metaprogramming is used.

 Can you please explain how are function names by variable at all related to 
 this? $object-foo() and $object-$foo() aren't even related in any way 
 semantically. If a conflict exists while grafting a trait, the original 
 method will be overshadowed silently and the method will be only accessible 
 by the alias. The entire idea of aliasing is to avoid a conflict with an 
 existing method of the same name. While if there's no collision, it's 
 accessible by both. So in practice it works by chance. How is working by 
 chance in any way aiding meta programming?

Let's use your example and make something more complex out of it:

trait T {
   function bar(){ $this-print();
$str = 'print';
$this-$str(); }
   function print() { echo 'trait'; }
}

class C {
   use T { print as foo; }
}

What is supposed to happen in this example if we assume renaming?
Would you expect the first function call to print() in bar() to work after the 
renaming?
Would you expect the second call, using metaprogramming, to work as well?


If we use aliasing instead of renaming, we have predictable behavior for all 
options.
foo just happens to be another alias for print.
If print is conflicting with something else, the conflict can be resolved, but 
print() is still going to be called reliably. If you do renaming, it will be 
partial, and you will get foo() called one time, and print() the other time.



 
 3. Properties silently collide in traits and classes.
 Please read 
 https://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate 
 carefully.
 
 Again, this is intended behavior.
 If you want to be notified of such collisions, please use E_STRICT.
 
 I've read them carefully now, but there's no solid reason given for this 
 behavior, or why the proper behavior only exists in E_STRICT.
 I can't really see state is a complex topic as a reason why trait property 
 collisions are handles inconsistently with method collisions.

Please do me the favor and consult the mailing list archives.
It is all in the various discussions.

There is no proper way to handle state, state requires splitting, merging, and 
what not. None of the research prototypes provides a simple solution.
The solution we have is a 'best effort' solution.
If you can do better, please make a proposal.


 4. The documentation says static propeties can't be defined by traits. Yet 
 they can.
 
 I don't know 

Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stan Vass

From that follows that traits override the super class's methods.
And, conflict resolution is only done between traits, not between a class 
and its traits.
The class body is a definite thing. Why would you purposefully add 
something to a class body that conflicts with a trait?
The class needs to know exactly what the traits are doing. There is no 
black-box reuse.


But that's precisely my point. No class would *purposefully* add something 
to conflict with a trait. Then by your own description the only case is 
*accidentally* adding something to conflict with it. In such a case why on 
Earth is it allowed without a fatal error?


The use case for overriding is typically that you got a conflict that you 
need to resolve by making a call to both conflicting methods.
Consider two traits that happen to provide a log() function, and in the 
resulting class you need to call both in all places log() has been called 
before.
Since the user of these traits is the only place where such semantic is 
sensibly defined, the method in the class body is overriding the traits 
methods, and can access aliases you

introduced for the traits' methods.


The way it works right now, on conflict, a class may access the trait method 
via the alias, or its own overriden copy by the conflicting name.
What is a trait to do? It can't access its own method at all as it doesn't 
know the alias.


The way this is implemented makes no sense, I'm afraid.


Let's use your example and make something more complex out of it:

trait T {
   function bar(){ $this-print();
$str = 'print';
$this-$str(); }
   function print() { echo 'trait'; }
}

 class C {
   use T { print as foo; }
}

What is supposed to happen in this example if we assume renaming?
Would you expect the first function call to print() in bar() to work after 
the renaming?

Would you expect the second call, using metaprogramming, to work as well?


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.


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.



Please do me the favor and consult the mailing list archives.
It is all in the various discussions.


If behavior specification and reasoning worked by consulting the archives 
for internals, we wouldn't have RFCs. I see no good reasoning in the RFC for 
this behavior, and going though thousands of emails in the archive is not a 
reasonable way to find out the reasoning.


There is no proper way to handle state, state requires splitting, merging, 
and what not. None of the research prototypes provides a simple solution.


State does not require splitting, merging. What does state splitting even 
mean? Here's a very simple design principle here: if you can't guarantee 
correctness for a behavior, provide a limited behavior, which guarantees 
correctness. This is how language design works. If there's a conflict, die 
with a fatal error, make people rename their properties so there's no 
conflict.


Assuming *silently* that the user is intentionally colliding state variables 
between traits because he/she wants to merge/split state is insanity IMO.



The solution we have is a 'best effort' solution.
If you can do better, please make a proposal.


I did in my original email. No property collisions. Fatal errors. It's very 
simple.


I assume we are talking about: 
http://php.net/manual/en/language.oop5.traits.php


Static variables and members/properties are not related at all. The writer 
of the documentation didn't get the wording correct. Please feel free to 
submit a patch.


I've submitted a doc bug. No one want to fix it since they think it's PHP 
that's wrong, not the manual. I can't do anything more about it.

Here's the link: https://bugs.php.net/bug.php?id=62156

Stan 



--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stefan Marr
Hi Stan:

On 01 Aug 2012, at 14:56, Stan Vass wrote:

 From that follows that traits override the super class's methods.
 And, conflict resolution is only done between traits, not between a class 
 and its traits.
 The class body is a definite thing. Why would you purposefully add something 
 to a class body that conflicts with a trait?
 The class needs to know exactly what the traits are doing. There is no 
 black-box reuse.
 
 But that's precisely my point. No class would *purposefully* add something to 
 conflict with a trait. Then by your own description the only case is 
 *accidentally* adding something to conflict with it. In such a case why on 
 Earth is it allowed without a fatal error?

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.



 
 The use case for overriding is typically that you got a conflict that you 
 need to resolve by making a call to both conflicting methods.
 Consider two traits that happen to provide a log() function, and in the 
 resulting class you need to call both in all places log() has been called 
 before.
 Since the user of these traits is the only place where such semantic is 
 sensibly defined, the method in the class body is overriding the traits 
 methods, and can access aliases you
 introduced for the traits' methods.
 
 The way it works right now, on conflict, a class may access the trait method 
 via the alias, or its own overriden copy by the conflicting name.
 What is a trait to do? It can't access its own method at all as it doesn't 
 know the alias.
 
 The way this is implemented makes no sense, I'm afraid.

Traits are not classes, traits are supposed to enable composition.
There is no static binding between trait methods. And, yes, this is by design.
It makes sense when you use traits for things you couldn't use classes for.

If you need strong consistency guarantees then you want to use classes.


 
 Let's use your example and make something more complex out of it:
 
 trait T {
   function bar(){ $this-print();
$str = 'print';
$this-$str(); }
   function print() { echo 'trait'; }
 }
 
  class C {
   use T { print as foo; }
 }
 
 What is supposed to happen in this example if we assume renaming?
 Would you expect the first function call to print() in bar() to work after 
 the renaming?
 Would you expect the second call, using metaprogramming, to work as well?
 
 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?


 
 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.


 There is no proper way to handle state, state requires splitting, merging, 
 and what not. None of the research prototypes provides a simple solution.
 
 State does not require splitting, merging. What does state splitting even 
 mean? Here's a very simple design principle here: if you can't guarantee 
 correctness for a behavior, provide a limited behavior, which guarantees 
 correctness. This is how language design works. If there's a conflict, die 
 with a fatal error, make people rename their properties so there's no 
 conflict.

http://scg.unibe.ch/archive/papers/Berg07aStatefulTraits.pdf


 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 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?

Traits offer already a much stronger safety-net than most other constructs in 
the language. And, thereby are way to strict for some people.

 The solution we have is a 'best effort' solution.
 If you can do better, please make a proposal.
 
 I did in my original email. 

Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stan Vass

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 

Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stan Vass
I'd like to apologize for my language. My goal isn't to insult anybody, 
especially PHP contributors of major features like traits.


I hope we can talk strictly implementation issues, I think I have a good 
idea how we can move this forward, I'll email you a list of behavior changes 
that I think make sense next 48 hours and we can discuss it offlist to keep 
the noise low here, if you'd like.


Stan 



--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Stefan Marr

On 01 Aug 2012, at 16:31, Stan Vass wrote:

 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?

Traits have a very simple semantics:
Step 1: identify which traits are to be used.
Step 2: resolve conflicts between traits.
Step 3: flatten the result into the class.
Step 4: do in the class what ever you are used to do in a normal class.

This results in a Glorified copy-and-paste..
This explains why traits are allowed to access private methods, because they 
are part of the class. They are flattened into it.
But, they are flattened into the class before the body of the class is 
processed.


 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.

Yes, I agree with you that there are known problems with state.

To quote myself: 
https://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate


Traits do not provide any provisioning for handling state. They are meant to 
provide a light-weight mechanism for flexible code reuse, with the main goal 
being to avoid code duplication.


Property support is a best-effort solution at the moment.

Following the rules outlined in the RFC:


• Properties are considered incompatible if they differ in their 
definition. This means, they differ in the applied modifiers (static, public, 
protected, private) or their initial value.
• Incompatible properties result in a fatal error.
• In all other cases, i.e., when the definitions are identical, an 
E_STRICT notice is shown to raise awareness about the potentially problematic, 
and discouraged use of properties.
• For those checks, all properties are treated equal. Properties from 
the base class and the composing class have to be compatible with properties 
from traits as well as the properties between all traits have to be compatible.
• Non-coliding properties, and properties which are not considered 
incompatible behave exactly the same as if they would have been defined in the 
composing class.


Please provide a fix in form of an RFC and a patch.
Thank you!



 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.

I disagree with the premise that renaming is necessary.


 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.

You are missing the point of the complexity that comes with the whole proposal.
Granting access, and merging add quite some complexity to the language (not 
only the implementation).
There have been proposal like a 'local' keyword for traits before.
They never got implemented.


 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.

How do you prevent a user from using dynamic (properties that are not 
predeclared) in a method that comes from a trait?



 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.

Again, what is your point? PHP is the wrong language for you then.
PHP is very dynamic and let's you shoot yourself in foot in all kind of strange 
ways.

Traits do not change these semantics. They try to 

Re: [PHP-DEV] Traits behavior still up in the air in 5.4

2012-08-01 Thread Christopher Jones



On 07/31/2012 04:23 PM, Stan Vass wrote:

I'd like to point out some puzzling behaviors in Traits as they exist in the 
production releases of PHP 5.4.


Regardless of the outcome of the mail thread, can you review the traits tests 
and create new tests for
any behaviour not already covered?

Chris

--
christopher.jo...@oracle.com
http://twitter.com/#!/ghrd

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



[PHP-DEV] Traits behavior still up in the air in 5.4

2012-07-31 Thread Stan Vass
I'd like to point out some puzzling behaviors in Traits as they exist in the 
production releases of PHP 5.4.


1. Name collisions between a trait method and a class method using the trait go 
unreported, the class silently shadowing the trait method:


trait T {
function foo() { $this-bar; }
function bar() { echo 'trait'; }
}

class C {
use T;
function bar() { echo 'class'; }
}

$c = new C;
$c-foo(); // class

Proposed behavior: Fatal error on collision, unless the method is imported with 
a unique name using the { ... as ... } syntax.


2. Using as syntax when importing a trait does NOT rename a method, but 
creates an alias CLONE, the original method still callable.


trait T {
function bar() { echo 'trait'; }
}

class C {
use T { bar as foo; }
}

$c = new C;
$c-bar(); // trait

Proposed behavior: the original name should be only accessible within the trait 
and its methods, not from the class methods or by calling the class instance's 
methods from outside.


3. Properties silently collide in traits and classes.


trait T1 {
private $foo;

trait T2 {
private $foo;
}

class C { use T1, T2; } // No error.

Proposed behavior: An error is produced only when the properties differ in 
visibility or a default value, which is clearly insufficient to determine 
they're used for the same purpose, and hold the same data. Instead they should 
use the same logic as method conflicts: fatal error on name collision. 
Alternatively, each trait property whould be accessible within the trait that 
defines it, not from other traits used in the same class, or the class itself.


4. The documentation says static propeties can't be defined by traits. Yet they 
can.


I don't know what's the bug here: a doc bug, or a code bug. For consistency, 
static properties should work, if instance properties work. Nothing is gained 
supporting it half-way.

Not many use 5.4 yet, so the sooner all this is clarified, the better, before 
it becomes completely unfixable due to BC. Feedback?

Stan