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 traits mechanism should be extended to allow
> more desirable behaviours to be programmed.
> 
> 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.


> 
>   use SaySomething {
>      sayIt = null;
>   }
>   use SaySomething {
>      unset sayIt;
>   }
>   use SaySomething {
>      sayIt = SaySomething::sayIt;
>   }
I would reject that based on the arguments from the beginning of the email.

Ok, thats enough for this email.
I will come back to the extension in a later email.

Thanks for the proposals
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

Reply via email to