Re: [PHP-DEV] Extensions to traits
On 13/02/11 9:15 PM, André Rømcke wrote: On Thu, Feb 10, 2011 at 6:25 PM, Ben Schmidt mail_ben_schm...@yahoo.com.auwrote: On 11/02/11 3:37 AM, Philip Olson wrote: You now have rights to the wiki rfc namespace. Thanks a lot, Philip. I have now made an RFC based on the most recent discussions: http://wiki.php.net/rfc/traitsmodifications I think this is a more solid proposal than my original one, and I hope we can continue to discuss it and agree to the extent that it's worth starting an implementation. Please read it and comment whenever you can find some time, guys! As for your first example: trait T { public function foo() { echohttp://www.php.net/echo T; }}class C { use T; public function foo() { echohttp://www.php.net/echo C; }} I think it would sometimes be desirable to allow this, for instance when a trait has been updated in a framework to adapt to what has become common practice in classes that uses it in the wild. ( I assume you already get error if function signature is different like in inheritance? ) So to allow both cases, what about letting people use the final keyword on functions to signal functions that can not be re declared without alias. Or better, add a new keyword since final should mean final. I don't mind that idea all that much, but perhaps doing the reverse makes more sense: allowing a trait author to add a keyword to methods which they intend and expect class authors to shadow. This means the status quo is leaning towards stability, not breakages. Perhaps we could reuse the 'default' keyword for this? That would indicate that the trait has provided a default implementation of this method, but expects the class author may well provide a more specialised implementation. Best practice would then dictate that a responsible trait author should only provide a default method for methods that were previously abstract methods of the trait, so things wouldn't break, but traits can still adapt to common practice, as you say. So then this would generate an error, which could be resolved with an insteadof in the use block: trait T { public function foo() { echo T; } } class C { use T; public function foo() { echo C; } } But this would not: trait T { default public function foo() { echo T; } } class C { use T; public function foo() { echo C; } } How does that sound? Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
That might seem odd but it's not inheritance. Yeah. And that's my main concern with it. It seems like inheritance (and is described like inheritance in the RFC at present--which is a documentation issue that will need to be addressed in the manual eventually), but it isn't. I feel it should be one or the other: either have full inheritance semantics, or have full conflict-resolution semantics like when trait methods conflict, at least by default. I think the latter is better. I'm really warming to the idea of using 'default' as I proposed in my last email, though, as essentially doing what André suggested 'in reverse'--i.e. a 'non-final' marker. Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
http://wiki.php.net/rfc/traitsmodifications Some thoughts: a) Class method conflict with trait Class implementation always wins I feel is the right way to think about traits. But 'abstract' already has special meaning, so maybe a keyword like 'final' could also do something special. André had the same kinds of feelings. I feel marking 'non-final' would be better, and suggest using 'default' for this purpose. See my earlier email for more details and reasoning. b) Support for 'non-breakable traits' - Add trait-local scope Really like the idea and how this could make traits less fragile. e.g. trait Foo { trait $bar; public $bar; } Could change the syntax to: trait Foo { var $bar = 'visibleByTrait'; // variable private to the trait -- zend_mangle_property_name('#Foo {NULL} bar') public $bar = 'visibleByClass'; // variable public to the class it gets composed in } class Test { use Foo; } $t = new Test; echo $t-bar; // 'visibleByClass' Seems like it could allow traits to have their own independent state (private properties), internally ~ zend_mangle_property_name('#' trait name {NULL} prop name). Small note: is there a plan to drop the T_VAR token? Any objections, concerns, more thoughts on the concept? I think it needs to work for methods as well as properties, so think the 'trait' keyword is better than the 'var' keyword for that reason. It would seem strange prefixing a method with 'var'. c) Extend 'use' syntax Didn't quite understand, will read again. Have you thought about how this could affect the reflection api and documentation? Yes, I had thought about it a bit. Now I've thought a bit further. When extended syntax is just moving class and trait methods around (essentially either duplicating them or omitting them), the reflection API should just work on the results of that movement. I presume that is what happens with traits. When extended syntax is generating forwarding functions, I expect the reflection API to return details of those forwarding functions. It can't do anything else, as due to PHP's dynamic nature, the functions being forwarded to are not immutable. I would document it as a forwarding function generation mechanism, too, so confusion is avoided. Having thought about it a bit further now, though, the reflection API is arguably a bit useless in this case. Perhaps to mitigate this, a new method could be added to ReflectionMethod to find details of the target of a forwarding method for a particular object (at the particular time the method is invoked). Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
RE: [PHP-DEV] Extensions to traits
On Sun Feb 13 05:15 AM, André Rømcke wrote: I have now made an RFC based on the most recent discussions: http://wiki.php.net/rfc/traitsmodifications I think it would sometimes be desirable to allow this, for instance when a trait has been updated in a framework to adapt to what has become common practice in classes that uses it in the wild. ( I assume you already get error if function signature is different like in inheritance? ) So to allow both cases, what about letting people use the final keyword on functions to signal functions that cannot be re declared without alias. Or better, add a new keyword since final should mean final. I find the implementation in trunk convenient, traits aren't meant to replace inheritance ~polymorphism. The 'final' keyword currently means nothing to the class: trait Foo { final static function test() { return 'Test'; } } class A { use Foo; static function test() { return 'Test2'; } } echo A::test(); // returns 'Test2' in trunk That might seem odd but it's not inheritance. There is no error if the class method signature is different from a trait. I'm comfortable with 'the class always wins', not really sure if should I be thinking differently... It makes a trait somewhat fragile but that's part of the design vs. grafts. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
RE: [PHP-DEV] Extensions to traits
On Thu Feb 10 12:25 PM, Ben Schmidt wrote: http://wiki.php.net/rfc/traitsmodifications Some thoughts: a) Class method conflict with trait Class implementation always wins I feel is the right way to think about traits. But 'abstract' already has special meaning, so maybe a keyword like 'final' could also do something special. b) Support for 'non-breakable traits' - Add trait-local scope Really like the idea and how this could make traits less fragile. e.g. trait Foo { trait $bar; public $bar; } Could change the syntax to: trait Foo { var $bar = 'visibleByTrait'; // variable private to the trait -- zend_mangle_property_name('#Foo {NULL} bar') public $bar = 'visibleByClass'; // variable public to the class it gets composed in } class Test { use Foo; } $t = new Test; echo $t-bar; // 'visibleByClass' Seems like it could allow traits to have their own independent state (private properties), internally ~ zend_mangle_property_name('#' trait name {NULL} prop name). Small note: is there a plan to drop the T_VAR token? Any objections, concerns, more thoughts on the concept? c) Extend 'use' syntax Didn't quite understand, will read again. Have you thought about how this could affect the reflection api and documentation? -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
On Thu, Feb 10, 2011 at 6:25 PM, Ben Schmidt mail_ben_schm...@yahoo.com.auwrote: On 11/02/11 3:37 AM, Philip Olson wrote: You now have rights to the wiki rfc namespace. Thanks a lot, Philip. I have now made an RFC based on the most recent discussions: http://wiki.php.net/rfc/traitsmodifications I think this is a more solid proposal than my original one, and I hope we can continue to discuss it and agree to the extent that it's worth starting an implementation. Please read it and comment whenever you can find some time, guys! As for your first example: trait T { public function foo() { echo http://www.php.net/echo T; }}class C { use T; public function foo() { echo http://www.php.net/echo C; }} I think it would sometimes be desirable to allow this, for instance when a trait has been updated in a framework to adapt to what has become common practice in classes that uses it in the wild. ( I assume you already get error if function signature is different like in inheritance? ) So to allow both cases, what about letting people use the final keyword on functions to signal functions that can not be re declared without alias. Or better, add a new keyword since final should mean final. My 2 Euro cents, André
Re: [PHP-DEV] Extensions to traits
On Feb 9, 2011, at 9:55 PM, Ben Schmidt wrote: - Don't write long e-mails to a mailing list, write an RFC http://wiki.php.net/rfc?do=register OK. I tried to do this. I got an account (username:isfs), but it seems it is nothing more than an unprivileged account--I don't seem to be able to edit or add pages at all. What do I need to do to be granted some? Greetings Ben, You now have rights to the wiki rfc namespace. Regards, Philip -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
On 11/02/11 3:37 AM, Philip Olson wrote: You now have rights to the wiki rfc namespace. Thanks a lot, Philip. I have now made an RFC based on the most recent discussions: http://wiki.php.net/rfc/traitsmodifications I think this is a more solid proposal than my original one, and I hope we can continue to discuss it and agree to the extent that it's worth starting an implementation. Please read it and comment whenever you can find some time, guys! Cheers, Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
- Don't write long e-mails to a mailing list, write an RFC http://wiki.php.net/rfc?do=register OK. I tried to do this. I got an account (username:isfs), but it seems it is nothing more than an unprivileged account--I don't seem to be able to edit or add pages at all. What do I need to do to be granted some? Thanks, Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
RE: [PHP-DEV] Extensions to traits
On Sat Jan 8 06:33 AM, Ben Schmidt wrote: Creating a patch will help getting feedback about what you're proposing http://ca3.php.net/reST/php-src/README.MAILINGLIST_RULES I hope I haven't broken any of the mailing list rules, but my apologies if I have, and please point out specifically where I've gone wrong. No rules broken in my opinion but in general consider: http://www.faqs.org/rfcs/rfc1855.html Over 100 lines is considered long The proposal (473 lines of plain text) would be easier to read on the web with some markup. As far as a patch goes, I don't think that is appropriate at this stage. I don't want to spend a lot of time creating a patch only to have it rejected if this could be avoided by a little discussion. My time is too valuable to me to waste like that, and with something as controversial as this, there's a real danger of that happening. Np, since you mentioned hacking the source I thought you had done some tinkering. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
Hi, Jonathan, On 7/01/11 4:42 AM, Jonathan Bond-Caron wrote: - New ideas are always welcome That's great to hear! - Don't write long e-mails to a mailing list, write an RFC http://wiki.php.net/rfc?do=register Sure. Once I've digested and responded to Stefan's replies, I'll work on putting something up there. Perhaps this advice would be worth adding to the mailing list rules page, as nothing about post length is mentioned there at all; it only talks about attachments. - Don't overuse the word 'simple' IMHO, if something is simple it should take 1-2 days to create a patch. That doesn't seem to be the case with what you're proposing. But feel free to prove me wrong :) Yeah, well, I meant simple conceptually, and relatively speaking, not that it wouldn't take time and effort. I don't think I'll be proving you wrong! But as I said, I am willing to get my hands dirty with this. Creating a patch will help getting feedback about what you're proposing http://ca3.php.net/reST/php-src/README.MAILINGLIST_RULES I hope I haven't broken any of the mailing list rules, but my apologies if I have, and please point out specifically where I've gone wrong. As far as a patch goes, I don't think that is appropriate at this stage. I don't want to spend a lot of time creating a patch only to have it rejected if this could be avoided by a little discussion. My time is too valuable to me to waste like that, and with something as controversial as this, there's a real danger of that happening. Cheers, Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
Hi, Stefan, Thanks for considering my ideas so carefully and for your detailed replies. The reason to go with insteadof is that the assignment syntax hides changes that might cause problems. Thus, when you change any of the traits participating in a composition in a way that a conflict would be introduced, the assignment syntax is hiding this. With the insteadof syntax you are actually forced to reevaluate whether your code still makes sense and include the offending change explicitly. OK. That makes sense. I'll rework any surviving parts of my proposal to be based on the insteadof syntax. This does not only have the mentioned benefit of making problematic changes in the traits hierarchy explicit, but the second reason to go with this design was the wish to have a 'exclude' operator without actually having a full-exclude operator. Thus, there is no way that you can leave out arbitrary methods from a trait. This seems wrong to me. Doesn't it go against the stated principle that the class author should have full control of traits? How is it full control if you can't exclude a method? What is the reasoning behind this wish not to have a full exclude operator? Hm, don't understand you here either. I needed to look up what impoverished actually means, you are not insulting me here, right? Just kidding ;) Sorry. I forget that a lot of you people aren't native English speakers and sometimes funny words like that slip in! I think all you people who communicate so fluently in additional languages are amazing. No error or warning will be generated, but the class will not work as intended; probably it will infinitely recurse; a nasty problem to track down. Furthermore, even if the incorrect method call didn't occur, there would be data-sharing problems, as both the ErrorReporting trait and the class' print() function make use of the $output data member, unintentially sharing data. Well, short answer: it is compiler-assisted copy-and-past, why isn't that trait just providing the glue that gets your class the necessary functionality to use a proper ErrorReporting class? Sorry, I know not a really helpful answer, but I hope that examples shows how I see traits. They are for reusing code in situations where the other concepts just break down. Not meant to replace those. I agree. I did say it was a poor example. I think the kinds of behavioural problems it demonstrates, though, could be found in situations where traits *are* truly appropriate. No need to argue over examples, though. Plenty of other things to argue about. :-) Warnings - - - - To avoid silent unintended shadowing, I suggest issuing a warning when a conflict between trait and class methods occurs. So this would trigger a warning: trait SaySomething { public function sayIt() { echo Something\n; } } class Sayer { use SaySomething; public function sayIt() { echo Hello world!\n; } } Ok, well, we could see the actual class body as another trait, and require it to follow the same composition rules, and that way make require the programmer to use insteadof in such a case. In return that would mean, that traits are not part of the inheritance chain at all anymore. Thus, first is the inheritance chain used to build up the implementation of a class, and afterwards all the traits are composed, while the original class is seen as another trait. That idea has certainly something to it. Yes, I think that would be good. Having read your other emails, as well as having allowed my proposal itself to clarify in my mind, I'm beginning to think it might be clearest and cleverest to avoid inheritance altogether with traits. Of course, this is in contrast to my original proposal, which was to increase traits' participation in inheritance. In this case, parent:: will always do what you expect then, and there's no need for prev::. Now to the other emails! Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
Hi again, Stefan, Continuing the conversation. On 7/01/11 10:18 AM, Stefan Marr wrote: On 02 Jan 2011, at 13:16, Ben Schmidt wrote: Extension - - - - - I suggest these two problems can be simply solved by introducing two additional uses of the trait keyword: as a scoping keyword and an access specifier. As a scoping keyword, it would be used analogously to self. Method calls such as $this-print() could be replaced with trait::print() when the programmer desires to ensure that their trait method, and only their trait method, is called--when there is no intention that overriding should be possible. It would only be able to be used in a trait, and could only be used to reference methods or properties defined in the same trait, using their original name. As an access specifier, it would be used instead of public, private, etc. in trait definitions, to mean that the member (data or method) can and can only be accessed using the mechanism above (trait::). Ok, that would actually get us around all the meta-programming problems. When you say that the 'trait'-access modifier always requires the access via a specific keyword (trait::) then mangling the name should be possible. On the other hand, what would iterating over the object properties show? Multiple properties with the same name, like with inherited private properties I suppose? Well, I hadn't thought about it before. :-) But yes, I think that makes perfect sense. And an occurrence of trait:: would mean, do a $this- but mangle the name first with the trait's name the original definition was in. Should be possible, but would certainly impact the Zend Engine a bit more than what we have now. One complication I hadn't thought of before is whether it should be possible to access trait:: methods and properties in different objects. And if it should be, what syntax to use. $that-trait::method() seems somewhat ugly to me. That would also suggest we should use $this-trait::method() for the same-object case. Certainly an interesting approach. Has someone else an opinion on that? I think actually this is the most important part of my proposal, so if it could be accepted, I would be very pleased. Obviously it does need a bit more thought and discussion yet, though. Overriding == Limitation -- At present, the overriding semantics of traits are that a method defined in a class proper overrides a method defined in a used trait which in turn overrides a method defined in an ancestor class. Bye the way, where comes that terminology from: class proper? We are talking about the body of a class, right? Yes. 'Class proper' = 'class itself'. We seem to use 'proper' in English as an adjective after a noun with a Latin-like sense of 'own/itself'. I guess we probably got it from French, and surprisingly it hasn't died out. Cheers, Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
Hi, Stefan, I think if the other stuff goes ahead, we can probably scrap what I originally proposed here. But perhaps something else would be helpful. I won't comment very specifically on aspects of my original proposal, but will just raise some new ideas for consideration and further thought. It all hinges on aliasing: how it works and how it can be avoided when necessary. Your comment on my example made this most clear. abstract class ActiveRecord { protected $new; protected $id; protected $other_values; protected function __construct($id,$values,$new) { $this-id=$id; $this-other_values=$values; $this-new=$new; } public function save() { if ($this-new) { if (!create_in_the_database()) return false; if ($this-id===null) $this-id=last_insert_id(); } else { if (!update_in_the_database()) return false; } return true; } public static function new() { return new static(null,static::$default_values,true); } public static function get($id) { return new static($id,get_from_the_database(),false); } } trait LoggingOperations { public function save() { if ($this-new) { log(Creating .get_called_class()); } else { log(Updating .get_called_class(). ID .$this-id); } if (!prev::save()) { log(Failed); return false; } log(Succeeded); return true; } } trait EnsuringNoConcurrentChanges { trait $original_values = array(); protected function setOriginalValues($values) { trait::$original_values = $values; } public static function get($id) { $record = prev::get($id); $record-setOriginalValues($record-other_values); return $record; } public function save() { $current_values=select_from_database(); if ($this-new$current_values) return false; if (!$this-new!$current_values) return false; if ($current_values!=trait::$original_values) return false; return prev::save(); } } trait UsingHashesForIDs { public function save() { if ($this-id===null) $this-id=random_hash(); return prev::save(); } } class SessionRecord extends ActiveRecord { protected static $default_values=array( 'user'='', 'time'='' ); use UsingHashesForIDs; } class Client extends ActiveRecord { protected static $default_values=array( 'user'='', 'name'='', 'address'='' ); use EnsuringNoConcurrentChanges, LoggingOperations { save = EnsuringNoConcurrentChanges::save, LoggingOperations::save; } } Ok, that example is, well, not actually helping you. You can do all the things here without your extensions, I believe. Before commenting speciically I'd like to point out that whether something *can* be done shouldn't be the only thing considered. Other important aspects are how easily it can be done, how tidily it can be done, how much code duplication it requires, and so on. Sometimes, a construct by merely providing elegance, without any additional power, is worthwhile. In fact, you *can* model any program as a Turing machine, so we actually need very little; but there aren't many popular languages like that! In this case, though, I think there are a couple of shortcomings in the current trait behaviour that mean this can't quite be done reliably. One thing of note is the use of the trait-scoped property, making the trait more robust. That was the subject of an earlier email, and one of the parts of the proposal you viewed most favourably, so I'm happy about that. I won't dwell on it here. Your problem with the save methods is solved by aliases and parent:: (parent:: only has semantics with respect to inheritance, so it will do the right thing. Your Client will have a save method that calls the aliased versions of the saves in the order you want. And in get() you can also just use parent::, no? Aliases come a long way, and to be honest, I am still coming to terms with how powerful they are, particularly in combination with traits' ability to have abstract methods. But there is at least one missing link. You can't reliably use parent:: in a trait method, because that jeopardises the reusability of the trait. In some composing classes, you might want it to call a method that isn't from the superclass. In this minimal example, parent:: happens to work; but that shouldn't be relied upon when writing a trait, and in the generalised case, where there are more traits and more composition combinations, it would fall apart. This means at the very least, you would have to use a method declared in the trait, and then write forwarding methods. In this case, you have basically
Re: [PHP-DEV] Extensions to traits
In fact, this is so nice, could I suggest it would be nice to allow other delegation-like forwarding to be done like this? You could have 'use' without a trait even, just like this: use { $queue-add as addToQueue; } Since the properties' object wouldn't be available at compile time, this extra ability would probably have to be implemented by basically generating an addToQueue method, and it wouldn't work with arguments passed by reference. It would basically be a shorthand for public function addToQueue() { return call_user_func(array($queue,'add'),func_get_args()); } but much more elegant. Of course should have been public function addToQueue() { return call_user_func(array($this-queue,'add'),func_get_args()); } Ben. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
RE: [PHP-DEV] Extensions to traits
On Sun Jan 2 07:16 AM, Ben Schmidt wrote: I would also like to propose some extensions to the functionality as currently described, which I think could potentially add tremendous power to the mechanism, with relatively little additional conceptual complexity and implementation effort. I've written it up as a bit of a proposal below. I'd love to hear what you think. - New ideas are always welcome - Don't write long e-mails to a mailing list, write an RFC http://wiki.php.net/rfc?do=register - Don't overuse the word 'simple' IMHO, if something is simple it should take 1-2 days to create a patch. That doesn't seem to be the case with what you're proposing. But feel free to prove me wrong :) Creating a patch will help getting feedback about what you're proposing http://ca3.php.net/reST/php-src/README.MAILINGLIST_RULES -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] Extensions to traits
Hi Ben: On 02 Jan 2011, at 13:16, Ben Schmidt wrote: While it's still in the pre-release stage, though, I would like to put in a vote for the assignment syntax: I think it is a lot easier to read and understand than the 'insteadof' syntax. The reason to go with insteadof is that the assignment syntax hides changes that might cause problems. Thus, when you change any of the traits participating in a composition in a way that a conflict would be introduced, the assignment syntax is hiding this. With the insteadof syntax you are actually forced to reevaluate whether your code still makes sense and include the offending change explicitly. There are two limitations of traits in their current implementation for which I would like to propose extensions. The first limitation is that traits can very easily break, particularly when methods are omitted from classes in which the rest of the trait is used Ehm, not sure I follow what you are getting at. One thing to emphasize here is the explicit design choice to use the insteadof. This does not only have the mentioned benefit of making problematic changes in the traits hierarchy explicit, but the second reason to go with this design was the wish to have a 'exclude' operator without actually having a full-exclude operator. Thus, there is no way that you can leave out arbitrary methods from a trait. That means, you cannot build compositions which suddenly miss certain expected method implementations. Well, of course, you can always build bugs into the code, but that is not different from the standard situation. You can always call arbitrary method names that just do not exist. Traits do not protect you from that. But the current design, protects you from explicitly allowing you to shot yourself in the foot. , or shadowed by method definitions in the class proper. The second limitation is that the trait overriding semantics are impoverished and needlessly restrictive. Hm, don't understand you here either. I needed to look up what impoverished actually means, you are not insulting me here, right? Just kidding ;) Incorrect method calls spring from the way trait methods can be omitted from classes where the rest of the trait is used, or shadowed by methods defined in the class proper. In either of these scenarios, any call in a trait to such a method may not call the method that was originally intended--they may fail, or they may call a different method, with unpredictable results. Of course, sometimes such a behaviour is desirable, if writing a trait which communicates with the rest of the class by means of method calls, yet provides a fallback methods in case the class author does not wish to provide such methods. However, when it is not intended, this could lead to incorrect and difficult-to-pinpoint behaviour. Ok, now I see what you are getting at. And my answer to that is: If you require your trait to guarantee a certain non-trivial invariants, then well, what you are actually doing is something which should become a class with all its benefits. Traits are not intended to replace classes as a mechanism to structure code, but they should supplement it. However, I am open to votes on these things since I do not have any reason to believe that my standpoint is especially useful or superior, thats just how I understand what traits are useful for. trait ErrorReporting { public function error($message) { $this-print($message); } private function print($message) { fputs($this-output,$message.\n); } } class Printer { use ErrorReporting; public $output=null; public function print($document) { /* Send the document to the printer--$this-output. */ /* ... */ if (there_was_an_error()) { $this-error(printing failed); } /* ... */ } } Ok, to summarize the example, you get a recursion because there is a naming problem, i.e., incompatibility between the used trait and the composing class. No error or warning will be generated, but the class will not work as intended; probably it will infinitely recurse; a nasty problem to track down. Furthermore, even if the incorrect method call didn't occur, there would be data-sharing problems, as both the ErrorReporting trait and the class' print() function make use of the $output data member, unintentially sharing data. Well, short answer: it is compiler-assisted copy-and-past, why isn't that trait just providing the glue that gets your class the necessary functionality to use a proper ErrorReporting class? Sorry, I know not a really helpful answer, but I hope that examples shows how I see traits. They are for reusing code in situations where the other concepts just break down. Not meant to replace those. Proposal I suggest these problems should be solved from two angles. Firstly, additional warnings should be triggered to alert the programmer to the problems, and secondly, the
Re: [PHP-DEV] Extensions to traits
Hi Ben: Here the second part, on your extension proposal. On 02 Jan 2011, at 13:16, Ben Schmidt wrote: Extension - - - - - I suggest these two problems can be simply solved by introducing two additional uses of the trait keyword: as a scoping keyword and an access specifier. As a scoping keyword, it would be used analogously to self. Method calls such as $this-print() could be replaced with trait::print() when the programmer desires to ensure that their trait method, and only their trait method, is called--when there is no intention that overriding should be possible. It would only be able to be used in a trait, and could only be used to reference methods or properties defined in the same trait, using their original name. As an access specifier, it would be used instead of public, private, etc. in trait definitions, to mean that the member (data or method) can and can only be accessed using the mechanism above (trait::). Ok, that would actually get us around all the meta-programming problems. When you say that the 'trait'-access modifier always requires the access via a specific keyword (trait::) then mangling the name should be possible. On the other hand, what would iterating over the object properties show? Multiple properties with the same name, like with inherited private properties I suppose? And an occurrence of trait:: would mean, do a $this- but mangle the name first with the trait's name the original definition was in. Should be possible, but would certainly impact the Zend Engine a bit more than what we have now. Certainly an interesting approach. Has someone else an opinion on that? Overriding == Limitation -- At present, the overriding semantics of traits are that a method defined in a class proper overrides a method defined in a used trait which in turn overrides a method defined in an ancestor class. Bye the way, where comes that terminology from: class proper? We are talking about the body of a class, right? Well, but I will stop here, and try to cover the rest in the next mail... 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] Extensions to traits
Hi Ben: On 02 Jan 2011, at 13:16, Ben Schmidt wrote: Proposal I would therefore like to propose an extension backwards-compatible with the current trait implementation. I will, however, extend the assignment syntax, rather than the 'insteadof' syntax, as I find that clearer, and more amenable to this extension. Of course, though, other syntaxes could be found. There are four aspects to this extension: (1) Introducing a new scoping keyword. (2) Allowing a method name to be used from multiple traits. (3) Allowing a trait to be included multiple times. (1) Introducing a new scoping keyword. - - - - - - - - - - - - - - - - - - - I suggest something such as 'prev', to refer to the previous definition of the method. Similar to 'parent', and the same in the absence of traits, this refers to the 'next higher definition in the trait hierarchy'; the 'trait hierarchy' is pictured like the 'class hierarchy' but including traits. So if 'prev' is used in a class method when a trait method of the same name exists, it will refer to the trait method, rather than referring to a method in a parent class. Alternatively, the 'parent' keyword meaning could be changed to have this meaning. My apologies if this is already the case: I have not played with the implementation in the trunk (though look forward to doing so at some stage) so am basing my comments purely on the RFC. (2) Allowing a method name to be used from multiple traits. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - When multiple methods of the same name are defined they simply take their place in the 'trait hierarchy' and can be accessed by means of 'prev' (see (1) above). So we could write, for instance: trait Hello { public function sayIt() { echo Hello ; } } trait World { public function sayIt() { prev::sayIt(); echo world ; } } class HelloWorld { use Hello, World { sayIt = Hello::sayIt, World::sayIt; } public function sayIt() { prev::sayIt(); echo !\n; } } $o = new HelloWorld(); $o-sayIt(); // Outputs Hello world !\n sayIt() in the class overrides sayIt() in World, which overrides sayIt() in Hello, but all are included. The first two make use of 'prev' to reference those higher up the hierarchy. Hm, after writing the part of my mail below, and coming back here, why can't you use aliases for that kind of problem? Instead of the prev:: stuff, you could just introduce an alias for the method you are 'hiding'. That should provide you with identical properties. And it would avoid 'magic' and weakening the flattening. Furthermore, traits are very explicit in what they do bring into your class at the moment, that would get lost with the approach required for 'prev::'. (3) Allowing a trait to be included multiple times. - - - - - - - - - - - - - - - - - - - - - - - - - - I still do not know what to say to the previous proposal, the 'prev' stuff. But, this one gets a reject, because you actually want to use classes here. I do not see the benefit of using a trait here instead of a class for your queues. Or you might want to elaborate why this design is better than one that uses proper objects. abstract class ActiveRecord { protected $new; protected $id; protected $other_values; protected function __construct($id,$values,$new) { $this-id=$id; $this-other_values=$values; $this-new=$new; } public function save() { if ($this-new) { if (!create_in_the_database()) return false; if ($this-id===null) $this-id=last_insert_id(); } else { if (!update_in_the_database()) return false; } return true; } public static function new() { return new static(null,static::$default_values,true); } public static function get($id) { return new static($id,get_from_the_database(),false); } } trait LoggingOperations { public function save() { if ($this-new) { log(Creating .get_called_class()); } else { log(Updating .get_called_class(). ID .$this-id); } if (!prev::save()) { log(Failed); return false; } log(Succeeded); return true; } } trait EnsuringNoConcurrentChanges { trait $original_values = array(); protected function setOriginalValues($values) { trait::$original_values = $values; } public static function get($id) { $record = prev::get($id); $record-setOriginalValues($record-other_values); return $record; } public function save() { $current_values=select_from_database(); if ($this-new$current_values) return false; if (!$this-new!$current_values) return false;
[PHP-DEV] Extensions to traits
Hello, PHP developers, I'm new to the list, but have been using PHP for a number of years, have done a little hacking in the source code, and have an interest in the development of the language. Particularly recently I have been reading with a good deal of excitement Stefan Marr's RFC on 'horizontal reuse' on the PHP Wiki: http://wiki.php.net/rfc/horizontalreuse The trait functionality looks like a great start and an innovative language development, and I'm looking forward to trying it out soon (when I can find some more time!), and particularly looking forward to making good use of it when it makes it into a release. While it's still in the pre-release stage, though, I would like to put in a vote for the assignment syntax: I think it is a lot easier to read and understand than the 'insteadof' syntax. I would also like to propose some extensions to the functionality as currently described, which I think could potentially add tremendous power to the mechanism, with relatively little additional conceptual complexity and implementation effort. I've written it up as a bit of a proposal below. I'd love to hear what you think. I would be willing to play a part implementing it, too. Cheers, Ben. = Proposed extensions to traits = Background == Traits in PHP [1] enable improved code reuse. They can be simplistically viewed as compiler-assisted copy-and-paste. Methods designed to be reused can be defined in traits and then these traits can be used in classes. The traits are 'flattened', so it is as if the trait methods were defined directly in the class in which they are used. Traits can access other methods and properties of the class, including those of other traits. They also fit in with the method overriding system: methods defined directly in a class override those in used traits, which in turn override those in ancestor classes. There are two limitations of traits in their current implementation for which I would like to propose extensions. The first limitation is that traits can very easily break, particularly when methods are omitted from classes in which the rest of the trait is used, or shadowed by method definitions in the class proper. The second limitation is that the trait overriding semantics are impoverished and needlessly restrictive. Breakability Limitation -- There are two main aspects of traits which make them easy to break: incorrect method calls and unintentionally shared state. Incorrect method calls spring from the way trait methods can be omitted from classes where the rest of the trait is used, or shadowed by methods defined in the class proper. In either of these scenarios, any call in a trait to such a method may not call the method that was originally intended--they may fail, or they may call a different method, with unpredictable results. Of course, sometimes such a behaviour is desirable, if writing a trait which communicates with the rest of the class by means of method calls, yet provides a fallback methods in case the class author does not wish to provide such methods. However, when it is not intended, this could lead to incorrect and difficult-to-pinpoint behaviour. The other way traits can break is by unintentionally sharing state. Traits may make use of the same data members (not recommended, but possible), or the same accessors, when each should actually have their own independent state. Again, this could lead to incorrect and difficult-to-pinpoint behaviour. Example --- trait ErrorReporting { public function error($message) { $this-print($message); } private function print($message) { fputs($this-output,$message.\n); } } class Printer { use ErrorReporting; public $output=null; public function print($document) { /* Send the document to the printer--$this-output. */ /* ... */ if (there_was_an_error()) { $this-error(printing failed); } /* ... */ } } This example is very contrived, and hopefully no programmer would be silly enough to fall into this exact trap. However, it is easy to imagine more subtle cases where this kind of thing could happen, particularly as traits and classes are modified from their original conception. The ErrorReporting trait allows the programmer to report errors in a consistent way by using the trait in many classes. It includes a print() method that is used to print the error to the screen. However, this method has been unintentionally shadowed by a print method in the class, intended to print a document on a printer. No error or warning will be generated, but the class will not work as intended; probably it will infinitely recurse; a nasty problem to track down. Furthermore, even if the incorrect method call didn't occur, there would be data-sharing problems, as both the ErrorReporting trait and the class' print() function make use of the $output data member,