Hi,
there is a lot of discussion going on about how traits should actually
work in PHP.
Currently, one of the main challenges seams to be to agree on a suitable
mechanism to avoid breaking traits and there behavior.
Eventually, there seams to be a large discomfiture on the excluding of
methods and the interweaving of methods from different traits.
I can agree on the opinion, that a unconditional exclude mechanism could
accidentally break traits.
But, I really do like the notion to be able to interweave traits and
compose something new from it, which results in an overlapping construct
using the appropriate methods for a concrete problem. Well, this implies
traits are not units of black-boxed reuse. They do act really
fine-grained and will require the knowledge about the used methods.
Therefore, remove/exclude is bad, we need to get rid of it.
There are some thoughts about aliasing. It seams to be not the
natural/closest solution. Renaming semantics would be more natural, but
it implies an exclude of the old method, too. Thus, renaming could
accidentally breaking a trait. Eventually, renaming needs to be avoided,
too.
Ok, lets get a step back and recall what traits have to achieve.
Traits try to be a construct to allow the reuse of a group of semantical
related methods.
They do not try to replace inheritance or the delegation patter. They
are still valid means to reuse "complex behavior and state" things.
Instead, the way to go with traits should be to reuse a small,
predominantly independent (but semantically related) number of methods
not justifying to build a full fledged class from them. Use traits to
build a class from them, which adds some additional semantics/behavior
to the set of methods got from the traits and build a complete blue
print for objects from it.
The main thing here seams to be that the current proposal does not
fulfill the claim to combine the traits properly in the case of
conflicts. Instead, there is a lot of potential to break the traits
behavior.
Let's try to find a notation where the traits are combined and conflicts
are solved upfront, before being applied to the class. (This is even the
way the implementation works right now.)
To get rid of exclude and rename I would like to propose the following:
//Example from the RFC with the cross-over conflict to be solved
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}
//here the new notion of combing traits and resolving conflicts upfront
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
A::bigTalk instead B::bigTalk;
A::bigTalk as talk;
}
}
The new use
is defined as use with a list of traits to be included
into the class.
Since the exclude is not appropriated, conflicts could be solved a lot
more explicitly with the new instead
keyword.
It has be read as: use B::smallTalk instead of A::smallTalk, which
solves the conflict explicitly and avoids the need for an exclude
operator with the power to exclude arbitrary methods.
If more traits are combined it could look like A:foo instead B::foo, C::foo;
To be able to reuse a method which is excluded this way the as
keyword can be used.
Read it like this: use A::bigTalk as talk in the class.
Think with this, it is not possible to remove a method somehow.
This could be even superior to the "hidding" thing and the notion of
"trait-local" methods since everything/any part of the semantics is
explicitly available in the notation. This has not been the case for
other notations so far. The trait-local things are nice, but complex and
I would prefer to avoid them, since the proposed traits have a very
clean and simple semantics. May be we could introduce them as addition
in a later release.
Kind Regards
Stefan
Hi,
there is a lot of discussion going on about how traits should actually
work in PHP.Currently, one of the main challenges seams to be to agree on a suitable
mechanism to avoid breaking traits and there behavior.
Eventually, there seams to be a large discomfiture on the excluding of
methods and the interweaving of methods from different traits.I can agree on the opinion, that a unconditional exclude mechanism could
accidentally break traits.
But, I really do like the notion to be able to interweave traits and
compose something new from it, which results in an overlapping construct
using the appropriate methods for a concrete problem. Well, this implies
traits are not units of black-boxed reuse. They do act really
fine-grained and will require the knowledge about the used methods.Therefore, remove/exclude is bad, we need to get rid of it.
There are some thoughts about aliasing. It seams to be not the
natural/closest solution. Renaming semantics would be more natural, but
it implies an exclude of the old method, too. Thus, renaming could
accidentally breaking a trait. Eventually, renaming needs to be avoided,
too.Ok, lets get a step back and recall what traits have to achieve.
Traits try to be a construct to allow the reuse of a group of semantical
related methods.
They do not try to replace inheritance or the delegation patter. They
are still valid means to reuse "complex behavior and state" things.
Instead, the way to go with traits should be to reuse a small,
predominantly independent (but semantically related) number of methods
not justifying to build a full fledged class from them. Use traits to
build a class from them, which adds some additional semantics/behavior
to the set of methods got from the traits and build a complete blue
print for objects from it.The main thing here seams to be that the current proposal does not
fulfill the claim to combine the traits properly in the case of
conflicts. Instead, there is a lot of potential to break the traits
behavior.Let's try to find a notation where the traits are combined and conflicts
are solved upfront, before being applied to the class. (This is even the
way the implementation works right now.)To get rid of exclude and rename I would like to propose the following:
//Example from the RFC with the cross-over conflict to be solved
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}//here the new notion of combing traits and resolving conflicts upfront
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
A::bigTalk instead B::bigTalk;
A::bigTalk as talk;
}
}The new
use
is defined as use with a list of traits to be included
into the class.
Since the exclude is not appropriated, conflicts could be solved a lot
more explicitly with the newinstead
keyword.
It has be read as: use B::smallTalk instead of A::smallTalk, which
solves the conflict explicitly and avoids the need for an exclude
operator with the power to exclude arbitrary methods.
If more traits are combined it could look likeA:foo instead B::foo, C::foo;
To be able to reuse a method which is excluded this way the
as
keyword can be used.
Read it like this: use A::bigTalk as talk in the class.Think with this, it is not possible to remove a method somehow.
This could be even superior to the "hidding" thing and the notion of
"trait-local" methods since everything/any part of the semantics is
explicitly available in the notation. This has not been the case for
other notations so far. The trait-local things are nice, but complex and
I would prefer to avoid them, since the proposed traits have a very
clean and simple semantics. May be we could introduce them as addition
in a later release.
I like this mechanism. This really does seem to be what I would use.
It also treats developers with a bit of respect that they understand
what they are doing and as such doesn't need to provide a wasteful
safety net. No magic.
A question though (and I don't have any sort of examples to explain
this better, so I hope you all follow).
If there are common names to methods in multiple traits (assume third
party libraries), and you one trait::method over another (A::bigTalk
instead B::bigTalk;), this will surely break the b trait? B trait may
well use its own bigTalk method. Instead it is going to end up using
A's bigTalk.
A "way" (more of an idea than a real solution and the syntax is wild
here) would be to support a "namespace" concept.
trait DTalker {
function dTalk() {
echo 'D';
}
}
trait CTalker requires DTalker {
function cTalk() {
$this->dTalk();
}
class Talker {
use ATalker as A, BTalker as B, CTalker
function usingTraits() {
$this->A::bigTalk();
$this->B::smallTalk();
$this->CTalker::cTalk();
// Maybe these are all the same method.
$this->CTalker::DTalker::dTalk();
$this->CTalker::dTalk();
$this->dTalk();
}
}
Traits as namespaces would solve the issue of conflict in the class
Talker (you would have to explicitly say which trait the method came
from.
I don't know if this can all be resolved at compile time. Surely the
use of call_user_func()
and others would impact somehow.
If trait C requires trait D, then this may lead to longer namespaces -
but from my understanding the purpose of namespaces is to resolve
collisions, so it cannot be avoided when there are collisions. (Though
my example doesn't collide - but that's not the point).
I hope this makes some sense.
Richard.
--
Richard Quadling
Zend Certified Engineer : http://zend.com/zce.php?c=ZEND002498&r=213474731
"Standing on the shoulders of some very clever giants!"
Hi Richard,
Richard Quadling schrieb:
Traits as namespaces would solve the issue of conflict in the class
Talker (you would have to explicitly say which trait the method came
from.
Yes, this would solve the conflicts, but is this really handy?
Every explicit reference to a trait/class name has to be adjusted if the
name changes or if there are any incompatible changes in the hierarchies.
Explicit references by names is probably always a solution, but not the
best. The inventors of the trait idea tried to avoid it in favor to
avoid the fragility introduced by it.
I don't know if this can all be resolved at compile time. Surely the
use ofcall_user_func()
and others would impact somehow.If trait C requires trait D, then this may lead to longer namespaces -
but from my understanding the purpose of namespaces is to resolve
collisions, so it cannot be avoided when there are collisions. (Though
my example doesn't collide - but that's not the point).I hope this makes some sense.
It would be a possible way, but personally I would not like to go there.
Kind Regards
Stefan
Hi Richard,
Richard Quadling schrieb:
Traits as namespaces would solve the issue of conflict in the class
Talker (you would have to explicitly say which trait the method came
from.Yes, this would solve the conflicts, but is this really handy?
Every explicit reference to a trait/class name has to be adjusted if the
name changes or if there are any incompatible changes in the hierarchies.
Explicit references by names is probably always a solution, but not the
best. The inventors of the trait idea tried to avoid it in favor to
avoid the fragility introduced by it.I don't know if this can all be resolved at compile time. Surely the
use ofcall_user_func()
and others would impact somehow.If trait C requires trait D, then this may lead to longer namespaces -
but from my understanding the purpose of namespaces is to resolve
collisions, so it cannot be avoided when there are collisions. (Though
my example doesn't collide - but that's not the point).I hope this makes some sense.
It would be a possible way, but personally I would not like to go there.
I can certainly understand your reluctance. It does make things look
complicated.
But my first reasoning for suggesting it is that we are have
namespaces in PHP6 primarily to reduce the conflicts between built in
classes and userland classes/libraries.
By having an alternative aliasing mechanism for traits, we now have 2
mechanisms to learn, both of which resolve names in some way. (For:
traits aren't namespaces, Against: Trait conflict resolution certainly
looks like it could be solved with namespaces).
Your primary argument against them of "has to be adjusted if the name
changes" holds true with namespaces any way. In fact if
method/class/interface/function X becomes
method/class/interface/function Y, I have to change something anyway.
So, this is the norm, not the exception.
My second reasoning is that namespaces act as encapulation. They are
still white-box. But encapsulated. So inside the trait all the methods
can talk to each other quite happily (statically or otherwise).
It just works and is based upon existing ideas (assuming we are in a PHP6).
--
Richard Quadling
Zend Certified Engineer : http://zend.com/zce.php?c=ZEND002498&r=213474731
"Standing on the shoulders of some very clever giants!"
Am 25.02.2008 um 20:33 schrieb Stefan Marr:
Hi,
there is a lot of discussion going on about how traits should actually
work in PHP.Currently, one of the main challenges seams to be to agree on a
suitable
mechanism to avoid breaking traits and there behavior.
Eventually, there seams to be a large discomfiture on the excluding
of methods and the interweaving of methods from different traits.I can agree on the opinion, that a unconditional exclude mechanism
could accidentally break traits.
But, I really do like the notion to be able to interweave traits and
compose something new from it, which results in an overlapping
construct using the appropriate methods for a concrete problem.
Well, this implies traits are not units of black-boxed reuse. They
do act really fine-grained and will require the knowledge about the
used methods.
Yes. This is how I understood them. I really don't think we need all
this complicated stuff for potential what-if situations that arise
from people not understanding how to use Traits. You are completely
right that Traits should not be regarded as black boxes.
I think I said it in an earlier mail - this entire discussion about
conflict resolving with aliasing etc is pretty void because it's
always assumed that Traits change, or developers using them don't
(want to?) know the inner workings. Traits are mixed into classes
statically, using an explicit syntactical facility. That means we can
assume the developer in question knows what he is doing. That also
means we can expect him to check for potential conflicts/breakage and
resolve it up front, as you're suggesting further down in your mail.
Therefore, remove/exclude is bad, we need to get rid of it.
There are some thoughts about aliasing. It seams to be not the
natural/closest solution. Renaming semantics would be more natural,
but it implies an exclude of the old method, too. Thus, renaming
could accidentally breaking a trait. Eventually, renaming needs to
be avoided, too.Ok, lets get a step back and recall what traits have to achieve.
Traits try to be a construct to allow the reuse of a group of
semantical related methods.
They do not try to replace inheritance or the delegation patter.
They are still valid means to reuse "complex behavior and state"
things.
Instead, the way to go with traits should be to reuse a small,
predominantly independent (but semantically related) number of
methods not justifying to build a full fledged class from them. Use
traits to build a class from them, which adds some additional
semantics/behavior to the set of methods got from the traits and
build a complete blue print for objects from it.The main thing here seams to be that the current proposal does not
fulfill the claim to combine the traits properly in the case of
conflicts. Instead, there is a lot of potential to break the traits
behavior.Let's try to find a notation where the traits are combined and
conflicts are solved upfront, before being applied to the class.
(This is even the way the implementation works right now.)
Winner. +1.
To get rid of exclude and rename I would like to propose the
following://Example from the RFC with the cross-over conflict to be solved
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}//here the new notion of combing traits and resolving conflicts
upfront
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
A::bigTalk instead B::bigTalk;
A::bigTalk as talk;
}
}The new
use
is defined as use with a list of traits to be
included into the class.
Since the exclude is not appropriated, conflicts could be solved a
lot more explicitly with the newinstead
keyword.
It has be read as: use B::smallTalk instead of A::smallTalk, which
solves the conflict explicitly and avoids the need for an exclude
operator with the power to exclude arbitrary methods.
If more traits are combined it could look likeA:foo instead B::foo, C::foo;
How about "B::smallTalk supersedes/replaces A::smallTalk" ?
David
Stefan Marr wrote:
<snip>To get rid of exclude and rename I would like to propose the following:
//Example from the RFC with the cross-over conflict to be solved
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}//here the new notion of combing traits and resolving conflicts upfront
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
A::bigTalk instead B::bigTalk;
A::bigTalk as talk;
}
}
This is far better than the original proposal.
My only objection is that this introduces two new keywords, trait and
instead. In addition, this can get very awkward if multiple traits
(more than 2) implement the same method name. I would prefer a simple
recycling of the "=" sign for both use cases (I'd also accept = for
override, "as" for alias).
class Talker {
use A, B, C, D {
smallTalk = A::smallTalk; // this says that if B, C or D implement
smallTalk, it is ignored
talk = A::bigTalk;
}
}
To everyone - this is an aliasing procedure, not a renaming one, and
won't affect internal methods of a trait (such as if B::whatever used
B::smallTalk), as Richard Quadling incorrectly suggested.
Greg
My only objection is that this introduces two new keywords, trait and
instead. In addition, this can get very awkward if multiple traits
(more than 2) implement the same method name. I would prefer a simple
recycling of the "=" sign for both use cases (I'd also accept = for
override, "as" for alias).class Talker {
use A, B, C, D {
smallTalk = A::smallTalk; // this says that if B, C or D implement
smallTalk, it is ignored
talk = A::bigTalk;
}
}
Well this is not just a different syntax, but an entirely different
approach. In Stefan's proposal one had to explicitly handle every
conflict manually. in your proposal you do not have to do this. As
trait's specifically wanted to get away from automatic conflict
resolution when things overlap, I think that Stefan's proposal makes
more sense.
BTW Stefan: Whats the syntax for when you want to override a trait
method with one inside the class definition?
I guess one would use "self::" like so:
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
self::bigTalk instead B::bigTalk, A::bigTalk;
A::bigTalk as talk;
}
function smallTalk() {
}
}
I also assume that we would mandate signature compatibility (which
IMHO should only throw an E_STRICT
and not a fatal like what we
currently do for inheritance in PHP6)?
regards,
Lukas
Lukas Kahwe Smith schrieb:
class Talker {
use A, B, C, D {
smallTalk = A::smallTalk; // this says that if B, C or D implement
smallTalk, it is ignored
talk = A::bigTalk;
}
}Well this is not just a different syntax, but an entirely different
approach. In Stefan's proposal one had to explicitly handle every
conflict manually. in your proposal you do not have to do this.
I share this objection. Maybe a somewhat handier solution of my proposal
would be the option to leave out the method name, but I'm not quite sure
whether it is really readable:
class Talker {
use A, B, C, D {
B::smallTalk instead A, C, D; //to be read like: use B::smallTalk
// instead the implementations form A, C, D
}
}
BTW Stefan: Whats the syntax for when you want to override a trait
method with one inside the class definition?I guess one would use "self::" like so:
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
self::bigTalk instead B::bigTalk, A::bigTalk;
A::bigTalk as talk;
}function smallTalk() {
}
}
Hm, personally, I would leave this out. The notion is that class
definitions will override trait methods in any case (even/especially if
traits methods are conflicting). So it would be fine to have this one
implicit. This would also avoid construction attempts like: A::bigTalk
instead self::bigTalk, A::bigTalk;
Kind Regards
Stefan
Hi,
I had a thought about recursion (and self referencing) inside trait defined
functions and the possible issues that might occur due to explicit/implicit
conflict resolution and or aliasing/renaming (i'm not completely
following what the status quo is regarding conflict resolution and/or
aliasing and/or renaming, but it seems all share issues to some degree)
if you wish to ensure that a trait's method specifically always calls
a method also defined in the same trait maybe syntax like the following could
be used:
trait Foo {
function A($x) {
if ($x > 0) trait::A($x--);
}
function B() {
trait::A(2);
}
function C() {
self::B();
}
}
here 'trait::' would tell the compiler to always use the actual method
from the trait in question (no idea how this would work in the guts of the
engine ... apologies for my ignorance :-) and 'self::' would refer to whatever
the method in question is as defined in the flatten, resulting class ...
so that 'self::B()' would call 'B()' as defined in the trait only if it wasn't
aliased/renamed/overloaded in the flatten, resulting class using said trait.
again I'm a little lost as to where the concept is at/going with regard to
aliasing/renaming/conflict-resolution ... but afaict the idea for 'trait::'
possibly offers a way out of potential problems (for the developer of a trait)
in any case.
thanks for listening ... sorry for the noise (if it is noise ... Stefan, Lukas please
be the judge :-)
rgds,
Jochem
Stefan Marr schreef:
Lukas Kahwe Smith schrieb:
class Talker {
use A, B, C, D {
smallTalk = A::smallTalk; // this says that if B, C or D implement
smallTalk, it is ignored
talk = A::bigTalk;
}
}Well this is not just a different syntax, but an entirely different
approach. In Stefan's proposal one had to explicitly handle every
conflict manually. in your proposal you do not have to do this.
I share this objection. Maybe a somewhat handier solution of my proposal
would be the option to leave out the method name, but I'm not quite sure
whether it is really readable:class Talker {
use A, B, C, D {
B::smallTalk instead A, C, D; //to be read like: use B::smallTalk
// instead the implementations form A, C, D
}
}BTW Stefan: Whats the syntax for when you want to override a trait
method with one inside the class definition?I guess one would use "self::" like so:
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
self::bigTalk instead B::bigTalk, A::bigTalk;
A::bigTalk as talk;
}function smallTalk() {
}
}
Hm, personally, I would leave this out. The notion is that class
definitions will override trait methods in any case (even/especially if
traits methods are conflicting). So it would be fine to have this one
implicit. This would also avoid construction attempts like: A::bigTalk
instead self::bigTalk, A::bigTalk;Kind Regards
Stefan
"Stefan Marr" php@stefan-marr.de wrote in message
news:47C574A9.1050600@stefan-marr.de...
class Talker {
use A, B, C, D {
B::smallTalk instead A, C, D; //to be read like: use B::smallTalk
// instead the implementations form A, C, D
}
}
Excuse me for just raising a suggestion:
How about "B::smallTask over A, C, D"?
(Use "over" instead of "instead")
Lukas Kahwe Smith wrote:
My only objection is that this introduces two new keywords, trait and
instead. In addition, this can get very awkward if multiple traits
(more than 2) implement the same method name. I would prefer a simple
recycling of the "=" sign for both use cases (I'd also accept = for
override, "as" for alias).class Talker {
use A, B, C, D {
smallTalk = A::smallTalk; // this says that if B, C or D implement
smallTalk, it is ignored
talk = A::bigTalk;
}
}Well this is not just a different syntax, but an entirely different
approach. In Stefan's proposal one had to explicitly handle every
conflict manually. in your proposal you do not have to do this. As
trait's specifically wanted to get away from automatic conflict
resolution when things overlap, I think that Stefan's proposal makes
more sense.BTW Stefan: Whats the syntax for when you want to override a trait
method with one inside the class definition?I guess one would use "self::" like so:
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
self::bigTalk instead B::bigTalk, A::bigTalk;
A::bigTalk as talk;
}function smallTalk() {
}
}I also assume that we would mandate signature compatibility (which
IMHO should only throw anE_STRICT
and not a fatal like what we
currently do for inheritance in PHP6)?
Hi,
OK, since none of my suggestions are acceptable, let me put it this way:
I am full -1 for traits if it introduces 2 new keywords. Find a
solution that does not, and I will change my vote.
Greg
Hi Gregory and others,
Traits rox! I will never use them cause I think in different way,
but for many people this will be a great feature.
Gregory, the comment you added here:
OK, since none of my suggestions are acceptable, let me put it this way:
Childish story! I want my toy!
I am fully +1 to traits, unfortunately I cannot spend enough time to help
with a useful idea :)
Best Regards, Dimitar Isusov
Hello Lukas,
you still cannot ignore basic inheritance or reuse rules. Protocols have
to be respected -> E_FATAL, fix your code.
marcus
Wednesday, February 27, 2008, 1:49:58 PM, you wrote:
My only objection is that this introduces two new keywords, trait and
instead. In addition, this can get very awkward if multiple traits
(more than 2) implement the same method name. I would prefer a simple
recycling of the "=" sign for both use cases (I'd also accept = for
override, "as" for alias).class Talker {
use A, B, C, D {
smallTalk = A::smallTalk; // this says that if B, C or D implement
smallTalk, it is ignored
talk = A::bigTalk;
}
}
Well this is not just a different syntax, but an entirely different
approach. In Stefan's proposal one had to explicitly handle every
conflict manually. in your proposal you do not have to do this. As
trait's specifically wanted to get away from automatic conflict
resolution when things overlap, I think that Stefan's proposal makes
more sense.
BTW Stefan: Whats the syntax for when you want to override a trait
method with one inside the class definition?
I guess one would use "self::" like so:
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
self::bigTalk instead B::bigTalk, A::bigTalk;
A::bigTalk as talk;
}
function smallTalk() {
}
}
I also assume that we would mandate signature compatibility (which
IMHO should only throw anE_STRICT
and not a fatal like what we
currently do for inheritance in PHP6)?
regards,
Lukas
Best regards,
Marcus
Stefan Marr wrote:
<snip>
<snip>//Example from the RFC with the cross-over conflict to be solved
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}//here the new notion of combing traits and resolving conflicts upfront
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
A::bigTalk instead B::bigTalk;
A::bigTalk as talk;
}
}
Ok, let me give an example code and ask a few questions about it:
trait A {
public function smallTalk() {
return 'a';
}
public function bigTalk() {
return strtoupper( $this->smallTalk() );
}
}
trait B {
public function smallTalk() {
return 'b';
}
public function bigTalk() {
return strtoupper( $this->smallTalk() );
}
}
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
A::bigTalk instead B::bigTalk;
A::bigTalk as talk;
}
}
// now to the questions
$talker = new Talker();
echo $talker->talk(); // What does this echo? My assumption is 'B'.
echo $talker->bigTalk(); // How about this?
echo $talker->smallTalk(); // This should be 'b'.
All the discussion seems to be on not breaking a traits methods and to
call internal methods from the trait. If this is the case, then the
answer to the second question would be 'A', which would not break the
trait's internal method call, but it seems less obvious to me.
--
"Joshua Thompson" spam.goes.in.here@gmail.com
<http://www.schmalls.com
Hi Joshua,
Joshua Thompson schrieb:
trait A {
public function smallTalk() {
return 'a';
}
public function bigTalk() {
return strtoupper( $this->smallTalk() );
}
}
trait B {
public function smallTalk() {
return 'b';
}
public function bigTalk() {
return strtoupper( $this->smallTalk() );
}
}
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
A::bigTalk instead B::bigTalk;
A::bigTalk as talk;
}
}
// now to the questions
$talker = new Talker();
echo $talker->talk(); // What does this echo? My assumption is 'B'.
echo $talker->bigTalk(); // How about this?
echo $talker->smallTalk(); // This should be 'b'.
Yes, right, the following class_at_runtime thing is would is produced
from the class definition and represents the class at runtime:
class_at_runtime Talker {
public function smallTalk() {
return 'b';
}
public function bigTalk() {
return strtoupper( $this->smallTalk() );
}
public function talk() {
return strtoupper( $this->smallTalk() );
}
}
All the discussion seems to be on not breaking a traits methods and to
call internal methods from the trait. If this is the case, then the
answer to the second question would be 'A', which would not break the
trait's internal method call, but it seems less obvious to me.
Yes, the trait is "broken" somehow. But IMHO that is the way traits
should work (in its basic variant). The opportunity here is to name it
not "break a trait" but interweave traits, instead. This will require a
notion of white-boxed reuse, which might not be everybody's intend.
If there is really the need for trait-local methods, I would really like
the local keyword, since private as a keyword has already a valid
semantics with traits.
Private states, this method is merged into the trait (in case it does
not conflict with another method from another trait and is excluded
therefore) and takes part in common inheritance, but is not inherited to
subclasses since it is private.
The local-keyword would be needed here:
//Example from the RFC with the cross-over conflict to be solved
trait A {
local function smallTalk() {
echo 'a';
}
public function bigTalk() {
return strtoupper( $this->smallTalk() );
}
}
trait B {
local function smallTalk() {
echo 'b';
}
public function bigTalk() {
return strtoupper( $this->smallTalk() );
}
}
class Talker {
use A, B {
// the smallTalk methods do not need to be handled here,
// since they are trait local and not visible to the class
A::bigTalk instead B::bigTalk;
B::bigTalk as talk; // in my original example the is A but B
// makes more sense
}
}
This would result in the following:
class_at_runtime Talker {
function \0A\0smallTalk() {
return 'a';
}
function \0B\0smallTalk() {
return 'b';
}
public function bigTalk() __Origin:A {
return strtoupper( $this->smallTalk() ); // here we will have to
// mess around with the method-lookup, to achieve a
// complete "alpha-renaming" (even for call_user_f*)
}
public function talk() __Origin:B {
return strtoupper( $this->smallTalk() ); //same as above
}
}
This would result in:
$t = new Talker();
echo $t->bigTalk(); // echos A
echo $t->talk(); // echos B
Maybe this is the way most users would be comfortable with, but it is a
bit more complex and I think we would need here the "local"-keyword
definitely.
Kind Regards
Stefan
<snip> // now to the questions $talker = new Talker(); echo $talker->talk(); // What does this echo? My assumption is 'B'. echo $talker->bigTalk(); // How about this? echo $talker->smallTalk(); // This should be 'b'.
Wouldn't traits as namespaces solve this?
It seems like an ideal solution.
--
Richard Quadling
Zend Certified Engineer : http://zend.com/zce.php?c=ZEND002498&r=213474731
"Standing on the shoulders of some very clever giants!"
-----Original Message-----
From: Stefan Marr [mailto:php@stefan-marr.de]
Sent: Monday, February 25, 2008 11:33 AM
To: internals Mailing List; Marcus Boerger
Subject: [PHP-DEV] How to build a real Trait thing without exclusion
and renaming//here the new notion of combing traits and resolving conflicts
upfront
class Talker {
use A, B {
B::smallTalk instead A::smallTalk;
A::bigTalk instead B::bigTalk;
A::bigTalk as talk;
}
}
Hi Stefan,
I think the discussion is going in the right direction but personally I
had to think really hard to figure out what this code was going to do :)
The following is a suggestion which takes into account some of the
discussions we've had including points both Lukas and Marcus have made
on this list. I have not thought through all of the implementation
details but I am quite sure we could implement such behavior.
The following code shows a few things:
- local properties which can be used in self-contained functionality.
The storage is guaranteed to stay internal and will not clash with the
aggregating class. - methods are by default hidden to the aggregating class and bound to
the trait (as Marcus pointed out the method name would be mangled with
the trait's name) - You can expose any method you want either by doing a "use TraitA *"
for all methods or by explicitly exposing methods to a specific method
in the class. Note: This is the equivalent of aliasing. The original
mangled method in the Trait doesn't disappear but you can override it
with a new method.
Unlike what some have said that this is better solved with MI or
Delegation as opposed to Traits I don't subscribe to that. I believe
this suggestion still gives you the benefits of copy&paste ala Traits
but adds more structure, a more useful ability to encapsulate
functionality which is very often the case when Trait like functionality
is needed (stateful traits), and most important, I think it's easy to
understand and read.
trait MyDebug {
local $counter = 1;
function showTrace() {
printf("Called this %d time(s)\n", $this->counter++);
`debug_backtrace()`;
}
}
trait MyTicks {
local $counter = 0;
function logIncrement() {
// Log when we incremented the counter
}
function incTicks() {
$this->logIncrement();
return $this->counter++;
}
}
class MyClock {
use MyDebug *;
use MyTicks {
timeInTicks = inTicks;
// Don't want to see logIncrement()
}
function display() {...}
}
Andi
Andi Gutmans wrote:
Hi Stefan,
I think the discussion is going in the right direction but personally I
had to think really hard to figure out what this code was going to do :)The following is a suggestion which takes into account some of the
discussions we've had including points both Lukas and Marcus have made
on this list. I have not thought through all of the implementation
details but I am quite sure we could implement such behavior.The following code shows a few things:
- local properties which can be used in self-contained functionality.
The storage is guaranteed to stay internal and will not clash with the
aggregating class.- methods are by default hidden to the aggregating class and bound to
the trait (as Marcus pointed out the method name would be mangled with
the trait's name)- You can expose any method you want either by doing a "use TraitA *"
for all methods or by explicitly exposing methods to a specific method
in the class. Note: This is the equivalent of aliasing. The original
mangled method in the Trait doesn't disappear but you can override it
with a new method.Unlike what some have said that this is better solved with MI or
Delegation as opposed to Traits I don't subscribe to that. I believe
this suggestion still gives you the benefits of copy&paste ala Traits
but adds more structure, a more useful ability to encapsulate
functionality which is very often the case when Trait like functionality
is needed (stateful traits), and most important, I think it's easy to
understand and read.trait MyDebug {
local $counter = 1;function showTrace() {
printf("Called this %d time(s)\n", $this->counter++);
debug_backtrace()
;
}
}trait MyTicks {
local $counter = 0;function logIncrement() {
// Log when we incremented the counter
}function incTicks() {
$this->logIncrement();
return $this->counter++;
}
}class MyClock {
use MyDebug *;
use MyTicks {
timeInTicks = inTicks;
// Don't want to see logIncrement()
}function display() {...}
}Andi
I was working on a detailed RFC of something along these lines. I didn't
include the local keyword, instead I went the route of stateful traits
and forced the definition of which scope in the class. I also tried to
keep the number of keywords to 1 (trait
).
I will keep working on it, and hopefully post it tomorrow for review.
--
"Joshua Thompson" spam.goes.in.here@gmail.com
<http://www.schmalls.com
-----Original Message-----
From: Joshua Thompson [mailto:spam.goes.in.here@gmail.com]
Sent: Wednesday, February 27, 2008 6:54 PM
To: internals@lists.php.net
Subject: Re: [PHP-DEV] How to build a real Trait thing without
exclusion and renamingI was working on a detailed RFC of something along these lines. I
didn't
include the local keyword, instead I went the route of stateful traits
and forced the definition of which scope in the class. I also tried to
keep the number of keywords to 1 (trait
).I will keep working on it, and hopefully post it tomorrow for review.
OK but I wouldn't get too caught up on the number of keywords although
we obviously don't want too many.
By the way one thing I left out of my proposal (mistakenly) was that
local can also be used on a function so that "use TraitA *" wouldn't
include it in the aggregating class.
Andi
Hi,
Joshua Thompson schrieb:
Andi Gutmans wrote:
The following code shows a few things:
- local properties which can be used in self-contained functionality.
The storage is guaranteed to stay internal and will not clash with the
aggregating class.
Yes, it is a nice to have.
- methods are by default hidden to the aggregating class and bound to
the trait (as Marcus pointed out the method name would be mangled with
the trait's name)
Hm, ok. It is the pessimistic counterpart to my proposal. In general,
I'm much more optimistic and would prefer to teach developers to use
proper naming to avoid conflicts. But if there is no other way to go, it
is ok.
I was working on a detailed RFC of something along these lines. I didn't
include the local keyword, instead I went the route of stateful traits
and forced the definition of which scope in the class. I also tried to
keep the number of keywords to 1 (trait
).I will keep working on it, and hopefully post it tomorrow for review.
Looking forward to read it :)
Kind Regards
Stefan
Hello Stefan,
Thursday, February 28, 2008, 8:30:48 AM, you wrote:
Hi,
Joshua Thompson schrieb:
Andi Gutmans wrote:
The following code shows a few things:
- local properties which can be used in self-contained functionality.
The storage is guaranteed to stay internal and will not clash with the
aggregating class.
Yes, it is a nice to have.
- methods are by default hidden to the aggregating class and bound to
the trait (as Marcus pointed out the method name would be mangled with
the trait's name)
Hm, ok. It is the pessimistic counterpart to my proposal. In general,
I'm much more optimistic and would prefer to teach developers to use
proper naming to avoid conflicts. But if there is no other way to go, it
is ok.
I did a lot of tutorials and sessions on object oriented PHP and I have
learned one thing we have to make it easy and limit the ability to use
stuff wrong. In other words we have to be pessimistic. Otherwise we end up
in a mess.
I was working on a detailed RFC of something along these lines. I didn't
include the local keyword, instead I went the route of stateful traits
and forced the definition of which scope in the class. I also tried to
keep the number of keywords to 1 (trait
).I will keep working on it, and hopefully post it tomorrow for review.
Looking forward to read it :)
Same here
Best regards,
Marcus
Hi!
trait MyDebug {
local $counter = 1;
IIRC we don't have keyword "local". Why not "private" or "static"?
class MyClock {
use MyDebug *;
use MyTicks {
timeInTicks = inTicks;
This looks like code which does assignment. How I am supposed to
understand from it that a new method for MyClock API is born?
In any case, why you need timeInTicks at all? If you need it public, why
not write an accessor? It'd be better OO anyway, since MyClock's clients
can't know about MyTicks's details or even its existence.
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
-----Original Message-----
From: Stanislav Malyshev [mailto:stas@zend.com]
Sent: Wednesday, February 27, 2008 7:07 PM
To: Andi Gutmans
Cc: php@stefan-marr.de; internals Mailing List
Subject: Re: [PHP-DEV] How to build a real Trait thing without
exclusion and renamingHi!
trait MyDebug {
local $counter = 1;IIRC we don't have keyword "local". Why not "private" or "static"?
I wouldn't get too caught up on the naming at this point but rather the
functionality.
class MyClock {
use MyDebug *;
use MyTicks {
timeInTicks = inTicks;This looks like code which does assignment. How I am supposed to
understand from it that a new method for MyClock API is born?In any case, why you need timeInTicks at all? If you need it public,
why
not write an accessor? It'd be better OO anyway, since MyClock's
clients
can't know about MyTicks's details or even its existence.
This is just an example of being able to alias a method from a trait.
Assuming two traits would use the same name this would give you the
ability to include it under a different name.
The point is we can alias but we can not remove.
Andi
This is just an example of being able to alias a method from a trait.
Assuming two traits would use the same name this would give you the
ability to include it under a different name.
The point is we can alias but we can not remove.
That's what I am asking - why alias? You have perfectly good name for
each one - A::whatever and B::whatever - which are unique. Why you need
any more aliases?
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
-----Original Message-----
From: Stanislav Malyshev [mailto:stas@zend.com]
Sent: Wednesday, February 27, 2008 11:21 PM
To: Andi Gutmans
Cc: php@stefan-marr.de; internals Mailing List
Subject: Re: [PHP-DEV] How to build a real Trait thing without
exclusion and renamingThis is just an example of being able to alias a method from a
trait.
Assuming two traits would use the same name this would give you the
ability to include it under a different name.
The point is we can alias but we can not remove.That's what I am asking - why alias? You have perfectly good name for
each one - A::whatever and B::whatever - which are unique. Why you
need
any more aliases?
The idea of Traits (whether you agree with it or not) is about a more
flexible way of assembling classes and reusing pieces of functionality.
The paradigm is different from inheritance/interfaces so the
A::whatever/B::whatever is really not relevant in this context unless I
have completely misunderstood you but I don't think I have.
Andi
Hello Stanislav,
as much as what you say is true, it forces you to type a lot which is
error prone. So when you want to make a function public then you need to do:
function whatever() {
A::whatever();
}
And actually you have to repeat the protocol and there the fun begins. Not
to begin even discussing the overhead of an additional userspace method
call that does not do anything. Aliasing is pretty helpful. But as Andi
said you might disagree.
marcus
Thursday, February 28, 2008, 8:21:22 AM, you wrote:
This is just an example of being able to alias a method from a trait.
Assuming two traits would use the same name this would give you the
ability to include it under a different name.
The point is we can alias but we can not remove.
That's what I am asking - why alias? You have perfectly good name for
each one - A::whatever and B::whatever - which are unique. Why you need
any more aliases?Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
Best regards,
Marcus
Hello Andi,
I agree with Stas about 'local' and actually his reasoning is why I
simply suggested 'private'. That also has the advantage that people already
know what it does.
marcus
Thursday, February 28, 2008, 5:14:17 AM, you wrote:
-----Original Message-----
From: Stanislav Malyshev [mailto:stas@zend.com]
Sent: Wednesday, February 27, 2008 7:07 PM
To: Andi Gutmans
Cc: php@stefan-marr.de; internals Mailing List
Subject: Re: [PHP-DEV] How to build a real Trait thing without
exclusion and renamingHi!
trait MyDebug {
local $counter = 1;IIRC we don't have keyword "local". Why not "private" or "static"?
I wouldn't get too caught up on the naming at this point but rather the
functionality.
class MyClock {
use MyDebug *;
use MyTicks {
timeInTicks = inTicks;This looks like code which does assignment. How I am supposed to
understand from it that a new method for MyClock API is born?In any case, why you need timeInTicks at all? If you need it public,
why
not write an accessor? It'd be better OO anyway, since MyClock's
clients
can't know about MyTicks's details or even its existence.
This is just an example of being able to alias a method from a trait.
Assuming two traits would use the same name this would give you the
ability to include it under a different name.
The point is we can alias but we can not remove.
Andi
Best regards,
Marcus
That's fine. I'm not attached to any name just the concept :)
-----Original Message-----
From: Marcus Boerger [mailto:helly@php.net]
Sent: Thursday, February 28, 2008 6:42 AM
To: Andi Gutmans
Cc: Stas Malyshev; php@stefan-marr.de; internals Mailing List
Subject: Re: [PHP-DEV] How to build a real Trait thing without
exclusion and renamingHello Andi,
I agree with Stas about 'local' and actually his reasoning is why I
simply suggested 'private'. That also has the advantage that people
already
know what it does.marcus
Thursday, February 28, 2008, 5:14:17 AM, you wrote:
-----Original Message-----
From: Stanislav Malyshev [mailto:stas@zend.com]
Sent: Wednesday, February 27, 2008 7:07 PM
To: Andi Gutmans
Cc: php@stefan-marr.de; internals Mailing List
Subject: Re: [PHP-DEV] How to build a real Trait thing without
exclusion and renamingHi!
trait MyDebug {
local $counter = 1;IIRC we don't have keyword "local". Why not "private" or "static"?
I wouldn't get too caught up on the naming at this point but rather
the
functionality.class MyClock {
use MyDebug *;
use MyTicks {
timeInTicks = inTicks;This looks like code which does assignment. How I am supposed to
understand from it that a new method for MyClock API is born?In any case, why you need timeInTicks at all? If you need it
public,
why
not write an accessor? It'd be better OO anyway, since MyClock's
clients
can't know about MyTicks's details or even its existence.This is just an example of being able to alias a method from a
trait.
Assuming two traits would use the same name this would give you the
ability to include it under a different name.
The point is we can alias but we can not remove.Andi
Best regards,
Marcus