Ahoi,
Thought it would be better to open up a new thread and also using the term "horizontal reuse" in the subject so that we make it clearer that there are actually two approaches. Here is the URL for Stefan's proposal:
http://wiki.php.net/rfc/horizontalreuse
"In case of the above definition of Talker, PHP will show a waring that there have been conflicts and name the methods smallTalk() and bigTalk() as the reason of this conflict. Therefore, neither of the given implementations will be available in the class."
I think this is a fundamental decision, should it be a warning or a fatal error? Generally I prefer PHP to keep on going whenever it can. I guess in most cases if we stick to a warning the user will end up with a fatal error anyway, but it might not be so clear why the given method is unavailable. But there should still be a warning, which I guess cannot be suppressed all that easily.
(@Stefan: didnt want to start editing minor typo's in the RFC .. s/waring/warning .. that typo can be found in other places too)
"Since the new name is recognized as an additional method, the bigTalk method still has to be excluded. Otherwise, PHP would print a warning that two methods from Traits have a conflict and are excluded. The introduction of a new name is not renaming and references in methods to a given method name aren't changed either. On the first look this may sound strange, but it provides the opportunity to build Traits and even hierarchies of Traits which fit together very well."
The third sentence is not so clear to me, but if I guess it its also just a typo as it makes more sense to me when replacing "renaming" to "result in renaming". But maybe you could tweak that paragraph to be a bit clearer. For example its still not totally clear to me why aliasing doesn't imply inclusion, I guess its definitely more flexible. How will things work when traits have overlapping method names when other methods of the given traits call these overlapping methods?
trait A {
public function smallTalk() {
$this->bigTalk();
}
public function bigTalk() {
echo 'A';
}
}
trait B {
public function smallTalk() {
$this->bigTalk();
}
public function bigTalk() {
echo 'B';
}
}
class Talker {
use A, B {
B::smallTalk instead A;
A::bigTalk instead B;
B::smallTalk as talk;
}
}
What is there anyway to ensure that when Talker::talk() is called that B::bigTalk() is still called and not A::bigTalk() because I want to maintain the "integrity" of each trait? Is that what you mean with its "not renaming and references in methods to a given method name aren't changed either" as in it will indeed call B::bigTalk()? Reading further long I however see that "This leads to missing features like recursion for methods introduced by aliases." Also I guess exactly this is the key difference to Grafts.
Reading further about Grafts, I must say I really like that approach. I mean we have talked about traits for quite a bit already, but I feel like I got how Grafts work the first time reading. I also like the fact that since Grafts are just classes, people can integrate them as they see fit, like they can use delegation if they are still on an older version of PHP or use Grafts. I also envision that there will be less surprises when using Grafts and this fits well with the approach to keeping the barrier to entry low for PHP development.
regards
Lukas Kahwe Smith
mls@pooteeweet.org
Ahoi,
Thought it would be better to open up a new thread and also using the
term "horizontal reuse" in the subject so that we make it clearer that
there are actually two approaches. Here is the URL for Stefan's
proposal:
http://wiki.php.net/rfc/horizontalreuse
One thing I feel is missing from the RFC is how is_a()
and instanceof are
affected with traits or grafts.
From:
http://github.com/gron/php-src/blob/PHP_5_3-traits/Zend/tests/traits/languag
e009.phpt
class Foo {
use C, A, B {
B::foo instead A, C;
}
}
$f = new Foo;
echo is_a($f, 'A'); ?
echo is_a($f, 'B'); ?
It's seem to me that a defining a 'trait' should be advertised strictly as
an 'advanced multiple inheritance technique' to reuse pieces of code and it
shouldn't be considered as an object (grafts proposal).
I don't like the "B::foo instead A, C" stuff though, so I agree with Lukas
and find that Grafts are more intuitive / easy to learn.
Personally, this syntax makes me happy but would keep the 'use' or 'with'
keyword:
http://wiki.php.net/rfc/nonbreakabletraits
trait A {
protected function foo() {
echo 'a';
}
}
trait D {
public function foo() { echo "d"; }
function bar() { echo "d"; }
}
class Foo {
use A;
use D {
function bar();
}
}
$f = new Foo;
echo is_a($f, 'A'); // true
echo is_a($f, 'D'); // false, since we've done an explicit
selection/brackets: use D {}
This is mainly userland feedback
Hi:
One thing I feel is missing from the RFC is how
is_a()
and instanceof are
affected with traits or grafts.
Well, my personal (I admit very academic) position is:
- Traits are not classes
- Traits are not interfaces
- Traits are not types
- Traits cannot be instantiated
Thus, there is no meaning of a is_a and instanceof, and it would not provide any meaningful information since you can exclude methods from a Trait in a composition.
Thus, you should resort to interfaces for use-case where you need to ensure that an object provides a certain set of methods.
Traits are purely for behavior, the class hierarchy or interface should provide the type-information/relations.
It's seem to me that a defining a 'trait' should be advertised strictly as
an 'advanced multiple inheritance technique' to reuse pieces of code and it
shouldn't be considered as an object (grafts proposal).
It is not an object, right. You can not instantiate traits.
But, I would not speak of multiple inheritance. I would prefer something along the lines of 'sustainable copy'n'past reuse'.
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
Hi:
One thing I feel is missing from the RFC is how
is_a()
and instanceof are
affected with traits or grafts.
Well, my personal (I admit very academic) position is:
- Traits are not classes
- Traits are not interfaces
- Traits are not types
- Traits cannot be instantiated
Thus, there is no meaning of a is_a and instanceof, and it would not provide any meaningful information since you can exclude methods from a Trait in a composition.
Thus, you should resort to interfaces for use-case where you need to ensure that an object provides a certain set of methods.
Traits are purely for behavior, the class hierarchy or interface should provide the type-information/relations.
It's seem to me that a defining a 'trait' should be advertised strictly as
an 'advanced multiple inheritance technique' to reuse pieces of code and it
shouldn't be considered as an object (grafts proposal).
It is not an object, right. You can not instantiate traits.
But, I would not speak of multiple inheritance. I would prefer something along the lines of 'sustainable copy'n'past reuse'.
I think Stefan used the right metaphor in the RFC: its language level copy paste.
regards,
Lukas Kahwe Smith
mls@pooteeweet.org
Stefan Marr wrote:
Well, my personal (I admit very academic) position is:
- Traits are not classes
- Traits are not interfaces
- Traits are not types
- Traits cannot be instantiated
Thus, there is no meaning of a is_a and instanceof
+1
--
Sebastian Bergmann Co-Founder and Principal Consultant
http://sebastian-bergmann.de/ http://thePHP.cc/
The third sentence is not so clear to me, but if I guess it its also
just a typo as it makes more sense to me when replacing "renaming" to
"result in renaming". But maybe you could tweak that paragraph to be a
bit clearer. For example its still not totally clear to me why
aliasing doesn't imply inclusion, I guess its definitely more
flexible.
It was really unclear to me too, this example cleared it up:
http://github.com/gron/php-src/blob/PHP_5_3-traits/Zend/tests/traits/bugs/al
ias-semantics.phpt
The current trait implementation does not 'rename' the previous method but
creates an 'alias'.
class Talker {
use A, B {
B::smallTalk instead A;
A::bigTalk instead B;
B::smallTalk as talk;
}
}
I think in this case, it would output 'A'.
Personally I would prefer to see:
class Talker {
use A {
public function bigTalk();
}
use B {
public function smallTalk();
public function smallTalk() as talk(); // this 'renames' the function /
not an alias
}
}
Other example:
class Talker {
use A;
use B {
public function smallTalk() as talk();
}
}
Hi Lukas,
Hi Jonathan:
Just a quick response on the renaming issue.
The third sentence is not so clear to me, but if I guess it its also
just a typo as it makes more sense to me when replacing "renaming" to
"result in renaming". But maybe you could tweak that paragraph to be a
bit clearer. For example its still not totally clear to me why
aliasing doesn't imply inclusion, I guess its definitely more
flexible.It was really unclear to me too, this example cleared it up:
http://github.com/gron/php-src/blob/PHP_5_3-traits/Zend/tests/traits/bugs/al
ias-semantics.phptThe current trait implementation does not 'rename' the previous method but
creates an 'alias'.
Yes, aliasing is kind of strange, and renaming might see more natural on first sight.
But, unfortunately, I do not see how that would fit with PHP.
My example why renaming is not possible is the following:
class Test {
function foo() {
echo "func: foo\n";
}
function bar() {
echo "func: bar\n";
}
}
$o = new Test;
$f = "foo";
$o->$f(); //this is valid PHP
$f = "bar";
$o->$f(); //this is valid PHP
So, now the only question which remains for me is, do we care?
Can we accept to break this code by introducing the notion of renaming?
What are the risks, and do the use-case for $o->$f(); matter?
I don't know. But do you use OR mappers, and if yes, how are they implemented?
It's just one possible use-case from the top of my head.
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
Just as a general idea (which is certainly something after traits are implemented once)
Scala offers stackable traits so that you can mixin traits during object creation.
An example:
trait Philosophical {
public function think () {
echo "Cogito ergo sum";
}
}
trait Drink {
public function drink () {
echo "gluck gluck"
}
}
$obj = new Person();
$obj->think(); // will fail
$obj = new Person() with Philosophical;
$obj->think(); // works
$obj = new Person() with Philsophical, Drink;
$obj->drink(); // works
$obj->think(); // works
This approach is taken in scala and works pretty fine there for composing classes
during runtime and should be douable in PHP too. For sure aliasing is not
possible in this example.
But as said just a general idea that I might try to work on once traits are comittet.
Stefan what do you think about "stackable traits" ?
- David
Just as a general idea (which is certainly something after traits are implemented once)
Scala offers stackable traits so that you can mixin traits during object creation.
An example:trait Philosophical {
public function think () {
echo "Cogito ergo sum";
}
}trait Drink {
public function drink () {
echo "gluck gluck"
}
}$obj = new Person();
$obj->think(); // will fail
$obj = new Person() with Philosophical;
$obj->think(); // works$obj = new Person() with Philsophical, Drink;
$obj->drink(); // works
$obj->think(); // worksThis approach is taken in scala and works pretty fine there for composing classes
during runtime and should be douable in PHP too. For sure aliasing is not
possible in this example.But as said just a general idea that I might try to work on once traits are comittet.
Stefan what do you think about "stackable traits" ?
Woha .. that code really scares me.
regards,
Lukas Kahwe Smith
mls@pooteeweet.org
This approach is taken in scala and works pretty fine there for composing classes
during runtime and should be douable in PHP too. For sure aliasing is not
possible in this example.But as said just a general idea that I might try to work on once traits are comittet.
Stefan what do you think about "stackable traits" ?
Woha .. that code really scares me.
I am not scared, it is just dynamic, and goes in the direction of prototype-based OO.
It could be implemented as hidden/shadow classes. Thus interpret
'new Person() with Philsophical, Drink;' as a class definition itself, with some name-mangling it would be possible. And should not result in noticeable overhead. At least that is where Self and V8(JavaScript) are getting their speed from.
But, well, it is extension, think we should get the basics right first.
Best regards
Stefan
regards,
Lukas Kahwe Smith
mls@pooteeweet.org--
--
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
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Am 25.03.10 16:21, schrieb Stefan Marr:
This approach is taken in scala and works pretty fine there for composing classes
during runtime and should be douable in PHP too. For sure aliasing is not
possible in this example.But as said just a general idea that I might try to work on once traits are comittet.
Stefan what do you think about "stackable traits" ?
Woha .. that code really scares me.
I am not scared, it is just dynamic, and goes in the direction of prototype-based OO.It could be implemented as hidden/shadow classes. Thus interpret
'new Person() with Philsophical, Drink;' as a class definition itself, with some name-mangling it would be possible. And should not result in noticeable overhead. At least that is where Self and V8(JavaScript) are getting their speed from.
yes that might be a good option, I'll keep it in mind for the case I
would write an RFC once we
got the basics right.
But, well, it is extension, think we should get the basics right first.
yes.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAkurgG4ACgkQ/snRGA1Vb0tEnQCbB7vECvafSy9ND1iXSciG10mU
k6kAoPJTAZmI5ZVN2WOFDgR6Ga/6moLY
=eMdt
-----END PGP SIGNATURE
Hi,
this was just brought up on IRC. my understanding is that traits have no concept of properties, but grafts do (all hidden internally). correct?
regards,
Lukas Kahwe Smith
mls@pooteeweet.org
Hi,
this was just brought up on IRC. my understanding is that traits have no concept of properties, but grafts do (all hidden internally). correct?
Right, the Traits proposal as it is at the moment, avoids the explicit discussion of state.
This is not a restriction per se, but could limit the composability of traits.
To ensure composibility, you would have to resort to abstract methods which are implemented by the composing class.
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
Hi,
this was just brought up on IRC. my understanding is that traits have no concept of properties, but grafts do (all hidden internally). correct?
Right, the Traits proposal as it is at the moment, avoids the explicit discussion of state.
This is not a restriction per se, but could limit the composability of traits.To ensure composibility, you would have to resort to abstract methods which are implemented by the composing class.
Maybe I should clarify that.
Currently the semantics is implicitly the following:
class Base{
var $a;
}
trait T1 {
var $a;
var $b;
}
trait T2 {
var $a;
var $c;
}
class Composite extends Base {
use T1, T2;
}
and Composite would have the instance variables $a, $b, $c.
The problem would be of course that Base, T1, and T2 could use $a for completely different things.
Simple, but at the cost of composability/safety.
But for most practical cases that should be 'good enough'. As already mentioned, if you want stronger guarantees, state accessor methods can be used.
However, I think this implication is not discussed in the RFC, and works just because PHP can create the fields dynamically. Thus, I used 'var' here in the example, instead of defining a semantics and handling for public, private, protected.
Best regards
Stefan
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--
--
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
Hi,
this was just brought up on IRC. my understanding is that traits have no concept of properties, but grafts do (all hidden internally). correct?
Right, the Traits proposal as it is at the moment, avoids the explicit discussion of state.
This is not a restriction per se, but could limit the composability of traits.To ensure composibility, you would have to resort to abstract methods which are implemented by the composing class.
Maybe I should clarify that.Currently the semantics is implicitly the following:
class Base{
var $a;
}trait T1 {
var $a;
var $b;
}trait T2 {
var $a;
var $c;
}class Composite extends Base {
use T1, T2;
}and Composite would have the instance variables $a, $b, $c.
The problem would be of course that Base, T1, and T2 could use $a for completely different things.Simple, but at the cost of composability/safety.
But for most practical cases that should be 'good enough'. As already mentioned, if you want stronger guarantees, state accessor methods can be used.However, I think this implication is not discussed in the RFC, and works just because PHP can create the fields dynamically. Thus, I used 'var' here in the example, instead of defining a semantics and handling for public, private, protected.
Right, but the issue doesnt go away if I would define a private property in the trait. Its still just "stupid" copy paste and so its the job of the trait user to ensure that there is no overlap. As such updating the trait code can easily break your code, its not encapsulated automatically, contrary to grafts. All in all I do see appeal for traits, but I think grafts just offer less wtf!?! surprises when code around you changes and so I favor grafts and I do not think we need both approaches.
So without having tested the patch, I am currently +1 on the grafts proposal as its in the wiki.
regards,
Lukas Kahwe Smith
mls@pooteeweet.org
Stefan Marr wrote:
Hi,
this was just brought up on IRC. my understanding is that traits have no concept of properties, but grafts do (all hidden internally). correct?
Right, the Traits proposal as it is at the moment, avoids the explicit discussion of state.
This is not a restriction per se, but could limit the composability of traits.To ensure composibility, you would have to resort to abstract methods which are implemented by the composing class.
Maybe I should clarify that.
Currently the semantics is implicitly the following:
class Base{
var $a;
}trait T1 {
var $a;
var $b;
}trait T2 {
var $a;
var $c;
}class Composite extends Base {
use T1, T2;
}and Composite would have the instance variables $a, $b, $c.
The problem would be of course that Base, T1, and T2 could use $a for completely different things.Simple, but at the cost of composability/safety.
But for most practical cases that should be 'good enough'. As already mentioned, if you want stronger guarantees, state accessor methods can be used.However, I think this implication is not discussed in the RFC, and works just because PHP can create the fields dynamically. Thus, I used 'var' here in the example, instead of defining a semantics and handling for public, private, protected.
Best regards
Stefan
Variable has been discuss in *http://tinyurl.com/y9t7nd9
--Mathieu Suen
Variable has been discuss in *http://tinyurl.com/y9t7nd9
Right, and related to that we have freezable traits
http://scg.unibe.ch/scgbib?_k=NNRwidu5&query=freezable+traits&display=abstract
Which influenced this RFC for PHP: http://wiki.php.net/rfc/nonbreakabletraits
And there are traits based on lexical nesting, which does not seem to be applicable to PHP at all:
http://scg.unibe.ch/scgbib?_k=J-wbMltV&query=tom+cutsem+bergel
However, it still comes with a relatively high complexity, and a lot new concepts...
The last time we had a lengthy discussion on this, and the conclusion was, people are afraid of this additional complexity.
That was my reason to design grafts.
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
Stefan Marr wrote:
Variable has been discuss in *http://tinyurl.com/y9t7nd9
Right, and related to that we have freezable traits
http://scg.unibe.ch/scgbib?_k=NNRwidu5&query=freezable+traits&display=abstractWhich influenced this RFC for PHP: http://wiki.php.net/rfc/nonbreakabletraits
And there are traits based on lexical nesting, which does not seem to be applicable to PHP at all:
http://scg.unibe.ch/scgbib?_k=J-wbMltV&query=tom+cutsem+bergelHowever, it still comes with a relatively high complexity, and a lot new concepts...
The last time we had a lengthy discussion on this, and the conclusion was, people are afraid of this additional complexity.
That was my reason to design grafts.Best regards
Stefan
Right I didn't like freezable traits as there are over-complicate for
almost nothing.
I don't see any relevant example for using the freeze operator.
And the unfreeze is even more obscure.
-- Mathieu Suen
Hi
Stefan what do you think about "stackable traits" ?
Woha .. that code really scares me.
While I like features like this in other languages, I think it would
be a big break with PHP's model. PHP has always had a rather static
object model, which may be somewhat limiting, but has the benefit of
being easy to comprehend. Traits/Grafts stays within this paradigm,
but making it possible to modify objects dynamically would be
stretching it.
--
troels
Hi,
"In case of the above definition of Talker, PHP will show a warning that there have been conflicts and name the methods smallTalk() and bigTalk() as the reason of this conflict. Therefore, neither of the given implementations will be available in the class."
I think this is a fundamental decision, should it be a warning or a fatal error? Generally I prefer PHP to keep on going whenever it can. I guess in most cases if we stick to a warning the user will end up with a fatal error anyway, but it might not be so clear why the given method is unavailable. But there should still be a warning, which I guess cannot be suppressed all that easily.
Well, I do not like a fatal errors. This problem does not leave the engine in an undefined state, and personally, I think, fatals should only be caused by something which really cannot be handled.
Since we have __call, there might even be something executable.
(@Stefan: didnt want to start editing minor typo's in the RFC .. s/waring/warning .. that typo can be found in other places too)
Thanks, fixed that.
"The introduction of a new name is not renaming and references in methods to a given method name aren't changed either."
The third sentence is not so clear to me, but if I guess it its also just a typo as it makes more sense to me when replacing "renaming" to "result in renaming". But maybe you could tweak that paragraph to be a bit clearer. For example its still not totally clear to me why aliasing doesn't imply inclusion, I guess its definitely more flexible. How will things work when traits have overlapping method names when other methods of the given traits call these overlapping methods?
I have not changed that paragraph, but I added a discussion section about renaming, and linked to it.
trait A {
public function smallTalk() {
$this->bigTalk();
}public function bigTalk() {
echo 'A';
}
}trait B {
public function smallTalk() {
$this->bigTalk();
}public function bigTalk() {
echo 'B';
}
}class Talker {
use A, B {
B::smallTalk instead A;
A::bigTalk instead B;
B::smallTalk as talk;
}
}What is there anyway to ensure that when Talker::talk() is called that B::bigTalk() is still called and not A::bigTalk() because I want to maintain the "integrity" of each trait? Is that what you mean with its "not renaming and references in methods to a given method name aren't changed either" as in it will indeed call B::bigTalk()? Reading further long I however see that "This leads to missing features like recursion for methods introduced by aliases." Also I guess exactly this is the key difference to Grafts.
Yes, that is the key difference.
Traits are meant to be light-weight and composeable. This includes, that I see it as a feature, that Traits can call methods of other Traits.
Reading further about Grafts, I must say I really like that approach. I mean we have talked about traits for quite a bit already, but I feel like I got how Grafts work the first time reading. I also like the fact that since Grafts are just classes, people can integrate them as they see fit, like they can use delegation if they are still on an older version of PHP or use Grafts. I also envision that there will be less surprises when using Grafts and this fits well with the approach to keeping the barrier to entry low for PHP development.
Well, how do we reach a conclusion on that whole topic?
With a poll?
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
"In case of the above definition of Talker, PHP will show a warning that there have been conflicts and name the methods smallTalk() and bigTalk() as the reason of this conflict. Therefore, neither of the given implementations will be available in the class."
I think this is a fundamental decision, should it be a warning or a fatal error? Generally I prefer PHP to keep on going whenever it can. I guess in most cases if we stick to a warning the user will end up with a fatal error anyway, but it might not be so clear why the given method is unavailable. But there should still be a warning, which I guess cannot be suppressed all that easily.
Well, I do not like a fatal errors. This problem does not leave the engine in an undefined state, and personally, I think, fatals should only be caused by something which really cannot be handled.Since we have __call, there might even be something executable.
Yeah, I agree, just wanted to bring this point up. I am right that this warning cannot be suppressed via some @ magic and so if at all would need to be suppressed by a custom error handler?
regards,
Lukas Kahwe Smith
mls@pooteeweet.org