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;
>         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.

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?

The only problem with aliases is that you do not get self-recursion, but you do 
not use that here.

> 
> 
> Final note regarding grafts
> ===========================
Grafts are dead. There is no implementation, and I do see traits and grafts as 
an either the one or the other, but not both. We do not need to make PHP more 
complex... 



All in all, I would like to have all the proposals recorded somewhere, in a way 
easily findable when you look up traits RFCs. Should be possible to group them 
in a namespace on the wiki, right?


Thanks again and sorry for 'rejecting' most of your ideas, but thats not final, 
I am still open for arguments.
By breaking up the topics into subthreads, it hopefully makes it easier for the 
community to comment on the different topics, too.

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

Reply via email to