Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:45814 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 95022 invoked from network); 14 Oct 2009 19:31:31 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 14 Oct 2009 19:31:31 -0000 Authentication-Results: pb1.pair.com header.from=php@stefan-marr.de; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=php@stefan-marr.de; spf=permerror; sender-id=unknown Received-SPF: error (pb1.pair.com: domain stefan-marr.de from 85.88.12.247 cause and error) X-PHP-List-Original-Sender: php@stefan-marr.de X-Host-Fingerprint: 85.88.12.247 toolslave.net Received: from [85.88.12.247] ([85.88.12.247:47913] helo=uhweb12247.united-hoster.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id D5/36-58792-F0726DA4 for ; Wed, 14 Oct 2009 15:31:28 -0400 Received: from soft83.vub.ac.be ([134.184.43.183]) by uhweb12247.united-hoster.com with esmtpsa (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.69) (envelope-from ) id 1My9Z6-0001Wg-Rt; Wed, 14 Oct 2009 21:31:21 +0200 Mime-Version: 1.0 (Apple Message framework v1074) Content-Type: text/plain; charset=us-ascii; format=flowed; delsp=yes In-Reply-To: <9D1CD88B-3A75-453C-9515-5F31E60F90AC@pooteeweet.org> Date: Wed, 14 Oct 2009 21:31:12 +0200 Cc: Content-Transfer-Encoding: 7bit Message-ID: <90CF2907-8C30-4EB0-9AC5-AD00EFC14023@stefan-marr.de> References: <002b01c92d67$ae92fdc0$0bb8f940$@de> <9D1CD88B-3A75-453C-9515-5F31E60F90AC@pooteeweet.org> To: Lukas Kahwe Smith X-Mailer: Apple Mail (2.1074) Subject: Re: [PHP-DEV] Request for Comments: Horizontal Reuse for PHP From: php@stefan-marr.de (Stefan Marr) On 14 Oct 2009, at 21:26, Lukas Kahwe Smith wrote: > So lets warm this up again. > HEAD is for development .. so lets get this into HEAD so that it > will be part of the next bigger PHP release for sure! Well, the code is sitting here http://github.com/gron/php-src/tree/PHP_6-traits and waits to be merged. :) Best regards Stefan > > regards, > Lukas > > On 13.10.2008, at 21:12, Stefan Marr wrote: > >> Hello, >> >> the last time the topic traits has been discussed is already a >> while ago. >> At this time, there have been two very different opinions out >> there, about >> how exactly this horizontal reuse should actually look like and no >> common >> consensus was found. In the following RFC, I describe both >> approaches in >> more detail. >> For neither of them is a work patch available, but this should not >> hinder >> discussion. In this step, we should find an agreement on the >> direction, in >> which PHP has to evolve. >> >> The part about Traits in this proposal, is equal to the former RFC. >> The >> second >> Part introduces the new variation and is an alternative approach to >> traits. >> >> Looking forward for your opinions, >> >> Kind Regards >> Stefan >> >> >> >> Request for Comments: Horizontal Reuse for PHP >> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> >> :Version: 2.0alpha >> :HTML: http://www.stefan-marr.de/artikel/rfc-horizontal-reuse-for-php.html >> :TXT: http://www.stefan-marr.de/rfc-horizontal-reuse-for-php.txt >> :Author: Stefan Marr >> :Previous RFC: http://www.stefan-marr.de/artikel/rfc-traits-for-php.html >> :Related RFC: http://wiki.php.net/rfc/nonbreakabletraits >> >> .. contents:: >> >> This RFC will discuss two different approaches to reuse behavior >> independently >> from the class hierarchy i.e. in an horizontal manner. The main >> motivation >> of both approaches is to provide a broader opportunity to model >> classes and >> class hierarchies with a clear conceptual background and optimal >> code reuse >> at >> the same time. Furthermore, the main distinction to other >> approaches is the >> explicit handling of conflicts resulting from overlapping >> interfaces in >> complex >> reuse scenarios. Both approaches would be valuable extensions to >> the PHP >> language, but both of them have their own benefits and drawbacks. >> Thus, this RFC is meant to provide a base for further discussion to >> be able >> to decide, which variant is the most PHP-like reuse mechanism. >> >> Why Do We Need Horizontal Reuse? >> ================================ >> >> Code reuse is one of the main goals that object-oriented languages >> try to >> achieve with inheritance. Unfortunately, single inheritance often >> forces >> the developer to take a decision in favor for either code reuse *or* >> conceptual clean class hierarchies. To achieve code reuse, methods >> have >> either to be duplicated or to be moved near the root of the class >> hierarchy, >> but this hampers understandability and maintainability of code. >> >> To circumvent this problems multiple inheritance and Mixins have been >> invented. >> But both of them are complex and hard to understand. PHP5 has been >> explicitly >> designed with the clean and successful model of Java in mind: >> single inheritance, but multiple interfaces. This decision has been >> taken >> to avoid the known problems of for example C++. The presented >> approaches >> have >> been designed to avoid those problems and to enable designers to >> build >> conceptually clean class hierarchies without the need to consider >> code reuse >> or complexity problems, but focusing on the real problem domain and >> maintainability instead. >> >> Limitations to Reuse in Single Inheritance Languages >> ---------------------------------------------------- >> >> There are several issues with reuse in PHP. To achieve as much >> reuse as >> possible you will probably move methods as high as possible in your >> inheritance >> hierarchy. At this point, there is a trade-off between conceptual >> consistency >> and reuse, because classes starts to have methods they do not need. >> So, when >> it >> is decided that the conceptual consistency is more valuable because >> of >> understandability of the class model, code duplication is caused or >> needs to >> be >> worked around by e.g. delegation, which is not always as nice as >> method >> implemented in the class tree. >> >> Beside conceptual issues, there are problems with third-party code >> you can >> not >> or might not want to modify. The following code illustrates the >> current >> implementation of an extended version of the PHP reflection API which >> provides >> detailed access to doc comment blocks. ``ReflectionMethod`` and >> ``ReflectionFunction`` are classes from the reflection API and have >> to be >> extended with exactly the same code. In this case it is impossible >> to change >> the classes itself, because they are not under our control i.e. >> they are >> implemented in C as part of the language. >> :: >> >> > class ezcReflectionMethod extends ReflectionMethod { >> /* ... */ >> function getReturnType() { /*1*/ } >> function getReturnDescription() { /*2*/ } >> /* ... */ >> } >> >> class ezcReflectionFunction extends ReflectionFunction { >> /* ... */ >> function getReturnType() { /*1*/ } >> function getReturnDescription() { /*2*/ } >> /* ... */ >> } >> ?> >> >> Thus, eventually, we end up with much duplicated code in both classes >> extending >> the original extension classes. >> >> Which Opportunities Does PHP Has? >> --------------------------------- >> >> For the sake of distinction and discussion, the two approaches >> are named differently. The idea of *Traits* for PHP has been already >> proposed >> in a former RFC_. This RFC introduces in addition the notion >> of *Grafts* (the term is borrowed from `agrarian cultivation`_). >> >> A *Trait* is an unit of behavioral reuse. It is very lightweight, >> stateless >> and >> allows for a very flexible composition of behavior into classes. >> A *Graft* is a class composed into another class. It is very much >> like >> grafting is >> done in the agriculture. It allows for a full-fledged reuse of >> classes >> inside >> of other classes independent of the class hierarchy. >> The following proposal will introduce both flavors of horizontal >> reuse and >> compares them in the context of PHP. >> >> Traits - Reuse of Behavior >> ========================== >> >> *Traits* is a mechanism for code reuse in single inheritance >> languages such >> as PHP. A Trait is intended to reduce some limitations of single >> inheritance >> by enabling a developer to reuse sets of methods freely in several >> independent >> classes living in different class hierarchies. >> The semantics of the combination of Traits and classes is defined >> in a way, >> which reduces complexity and avoids the typical problems associated >> with >> multiple inheritance and Mixins. >> >> They are recognized for their potential in supporting better >> composition >> and reuse, hence their integration in languages such as Perl 6, >> Squeak, >> Scala, Self, Slate and Fortress. Traits have also been ported to >> Java and >> C#. >> In the following, the concepts behind Traits will be adapted for >> PHP to >> propose >> two different approaches which resemble the main ideas. >> >> A Trait is similar to a class, but only intended to group >> functionality in a >> >> fine-grained and consistent way. It is not possible to instantiate >> a Trait >> on >> its own. It is an addition to traditional inheritance and enables >> horizontal >> composition of behavior. >> >> In the introduction an example has been given illustrating reuse >> limitations >> of single inheritance. With Traits it is possible to remove the >> duplicated >> code >> without compromising conceptual consistency. >> :: >> >> > trait ezcReflectionReturnInfo { >> function getReturnType() { /*1*/ } >> function getReturnDescription() { /*2*/ } >> } >> >> class ezcReflectionMethod extends ReflectionMethod { >> use ezcReflectionReturnInfo; >> /* ... */ >> } >> >> class ezcReflectionFunction extends ReflectionFunction { >> use ezcReflectionReturnInfo; >> /* ... */ >> } >> ?> >> >> This is just a small example of what Traits are useful for. >> The next sections will discuss more advanced techniques and >> describe how >> Traits are used in PHP. >> >> The Flattening Property >> ----------------------- >> >> As already mentioned, multiple inheritance and Mixins are complex >> mechanisms. >> Traits are an alternative which have been designed to impose no >> additional semantics on classes. Traits are only entities of the >> literal >> code >> written in your source files. There is no notion about Traits at >> runtime. >> They are used to group methods and reuse code and are totally >> flattened >> into the classes composed from them. It is almost like a language >> supported >> and >> failsafe copy'n'paste mechanism to build classes. >> >> Even though, there is no runtime notion of Traits, since they are >> part of >> the >> source code and thus, define the structure of the system, >> reflection about >> Traits still is possible, but they do not influence the runtime >> behavior of >> the >> system. >> >> Precedence Order >> """""""""""""""" >> >> Flattening is achieved by applying some simple rules on the >> composition >> mechanism. Instead of implementing a fancy and awkward algorithm to >> solve >> problems, the entire control about the composition is left in the >> hand of >> the >> developer and fits nicely into the known inheritance model of PHP. >> The following examples illustrate the semantics of Traits and their >> relation >> to methods defined in classes. >> :: >> >> > class Base { >> public function sayHello() { >> echo 'Hello '; >> } >> } >> >> trait SayWorld { >> public function sayHello() { >> parent::sayHello(); >> echo 'World!'; >> } >> } >> >> class MyHelloWorld extends Base { >> use SayWorld; >> } >> >> $o = new MyHelloWorld(); >> $o->sayHello(); // echos Hello World! >> ?> >> >> As shown in the above code, an inherited method from a base class is >> overridden >> by the method inserted into ``MyHelloWorld`` from the ``SayWorld`` >> Trait. >> The behavior is the same for methods defined in the >> ``MyHelloWorld`` class. >> The precedence order is that methods from the current class >> override Trait >> methods, >> which in return override methods from the base class. >> :: >> >> > trait HelloWorld { >> public function sayHello() { >> echo 'Hello World!'; >> } >> } >> >> class TheWorldIsNotEnough { >> use HelloWorld; >> public function sayHello() { >> echo 'Hello Universe!'; >> } >> } >> >> $o = new TheWorldIsNotEnough(); >> $o->sayHello(); // echos Hello Universe! >> ?> >> >> Multiple Traits Usage >> """"""""""""""""""""" >> >> To keep things simple in the beginning, there has only one Trait >> being used >> at >> a time, but obviously a class could use multiple Traits at the same >> time. >> :: >> >> > trait Hello { >> public function sayHello() { >> echo 'Hello '; >> } >> } >> >> trait World { >> public function sayWorld() { >> echo ' World'; >> } >> } >> >> class MyHelloWorld { >> use Hello, World; >> public function sayExclamationMark() { >> echo '!'; >> } >> } >> >> $o = new MyHelloWorld(); >> $o->sayHello(); >> $o->sayWorld(); >> $o->sayExclamationMark(); >> // Results eventually in: Hello World! >> >> Conflict Resolution >> """"""""""""""""""" >> >> Traits are already used in different programming languages and it >> has shown >> that conflicts will occur, but they are the exception, not the >> rule. In most >> systems under investigation, the mechanisms to resolve conflicts >> have been >> used >> very infrequently, but also have proven to be a valuable >> mechanisms. Since >> it >> increases the composition power of the developers. >> One example for a typical conflict are different Traits providing >> methods >> with the >> same name. >> :: >> >> > trait A { >> public function smallTalk() { >> echo 'a'; >> } >> public function bigTalk() { >> echo 'A'; >> } >> } >> >> trait B { >> public function smallTalk() { >> echo 'b'; >> } >> public function bigTalk() { >> echo 'B'; >> } >> } >> ?> >> >> Both classes have to be used in a class named ``Talker``. Multiple >> inheritance >> and Mixins define an algorithm to resolve this conflict. Traits >> don't. >> Conflicts >> are not solved implicitly by any kind of precedence. Instead, to >> avoid >> implicit >> complexity, the developer has full control over class composition. >> :: >> >> > class Talker { >> use A, B; >> } >> ?> >> >> 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. >> >> Instead, the developer can exactly define which methods are used >> and how the >> conflict is resolved. >> :: >> >> > class Talker { >> use A, B { >> B::smallTalk instead A::smallTalk, >> A::bigTalk instead B::bigTalk >> } >> } >> ?> >> >> This definition will result in leaving out ``smallTalk()`` from >> Trait A >> and ``bigTalk()`` from Trait B. Therefore, the resulting class >> Talker would >> echo ``'b'`` for ``smallTalk()`` and ``'A'`` for ``bigTalk().`` >> But this simple form of exclusion of methods is not the best choice >> for all >> situations. >> :: >> >> > class Talker { >> use A, B { >> B::smallTalk instead A::smallTalk, >> A::bigTalk instead B::bigTalk, >> B::bigTalk as talk >> } >> } >> ?> >> >> Beside leaving out methods it is possible to introduce a new name >> for a >> method >> from a Trait. This is done like ``originalMethodName as >> additionalMethodName``. >> In the example above, it has to be read as ``use B::bigTalk as talk >> in class >> Talker``. This does not imply any renaming, instead ``talk`` is >> introduced >> as >> an additional name for this method. Thus, recursion inside of >> ``talk`` will >> still call a method with the name bigTalk. >> The resulting ``Talker`` class will consist of following three >> methods: >> >> * ``bigTalk() { echo 'A'; }`` >> * ``smallTalk() { echo 'b'; }`` >> * ``talk() { echo 'B'; }`` >> >> 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. >> >> Traits Composed from Traits >> """"""""""""""""""""""""""" >> >> Not explicitly mentioned jet, but implied by the flattening >> property is the >> composition of Traits from Traits. >> Since Traits are fully flattened away at compile time it is >> possible to use >> Traits to compose Traits without any additional impact on the >> semantics. >> The following code illustrates this:: >> >> > trait Hello { >> public function sayHello() { >> echo 'Hello '; >> } >> } >> >> trait World { >> public function sayWorld() { >> echo 'World!'; >> } >> } >> >> trait HelloWorld { >> use Hello, World; >> } >> >> class MyHelloWorld { >> use HelloWorld; >> } >> >> $o = new MyHelloWorld(); >> $o->sayHello(); >> $o->sayWorld(); >> // Results eventually in: Hello World! >> ?> >> >> Traits itself can take part in arbitrary compositions, but Traits >> are not >> part >> of the inheritance tree i.e., it is not possible to inherit from a >> Trait to >> avoid confusion and misuse of Traits. >> >> Express Requirements by Abstract Methods >> """""""""""""""""""""""""""""""""""""""" >> >> Since Traits do not contain any state/properties, there is a need to >> describe >> the requirements a Trait will rely on. In PHP it would be possible to >> utilize >> the dynamic language features, but it is a common practice to give >> this >> requirements explicitly. >> This is possible with abstract methods like it is used for abstract >> classes. >> :: >> >> > trait Hello { >> public function sayHelloWorld() { >> echo 'Hello'.$this->getWorld(); >> } >> abstract public function getWorld(); >> } >> >> class MyHelloWorld { >> private $world; >> use Hello; >> public function getWorld() { >> return $this->world; >> } >> public function setWorld($val) { >> $this->world = $val; >> } >> } >> ?> >> >> The usage of abstract methods allows to state not always obvious >> relation >> ships >> and requirements explicitly. It is favored over the implicit usage >> of the >> dynamic method resolution and property creation in the context of >> complex >> projects for the sake of readability. >> >> Traits Semantics Summarized >> --------------------------- >> >> 1. Traits do not add runtime semantics, they only take part in the >> process >> of >> building a class. >> 2. Traits integrate into the precedence order of method overriding. >> 3. To avoid complexity, conflicts between Trait methods have to be >> solved >> explicitly. Otherwise a waring is generated and the conflicting >> methods >> are excluded. >> 4. In combinations with conflicts, developers have to chose >> explicitly which >> method has to be used, methods not chosen will be excluded from the >> composition. >> 5. Aliases can be defined for methods to enable reuse of conflicting >> methods. >> 6. Traits can be composed from Traits. >> 7. Traits can state requirements explicitly by the use of abstract >> methods. >> >> As a result of this semantics, at runtime, classes build using >> Traits are >> not distinguishable >> from classes not using Traits but traditional code duplication >> instead. >> Semantics of ``parent`` and ``$this`` hasn't changed, too. Used in >> a Trait >> method, they behave exactly the same as if the method has been >> defined in >> the >> class directly. >> >> Visibility >> ---------- >> >> Visibility modifiers have not been discussed so far. Since Traits >> are meant >> as >> units of reuse, modifiers should be changeable easily in the >> context of a >> composed class. Therefore, the aliasing operation is able to change >> the >> visibility modifier of a method, too. >> :: >> >> > trait HelloWorld { >> public function sayHello() { >> echo 'Hello World!'; >> } >> } >> >> class MyClass1 { >> use HelloWorld { sayHello as protected } >> } >> >> class MyClass2 { >> use HelloWorld { doHelloWorld as private sayHello } >> } >> ?> >> >> The final modifier is supported, too. The static modifier is not >> supported, >> because it would change the methods semantics and references to `` >> $this`` >> would break. >> >> Grafts - Class Composition >> ========================== >> >> A *Graft* is a class composed into another class to reuse it avoiding >> inheritance and without an explicit need for delegation. The most >> important >> difference to a Trait is the possibility to define state in >> addition to >> behavior inside the reused entity. Furthermore, the grafting >> approach is not >> >> about reuse methods in a manner focused on flexibility, but instead >> it is >> about reusing small encapsulated units of behavior and state to build >> classes >> from them. Thus, it could be viewed as a form of private multiple >> inheritance >> which avoids conflicts by keeping everything private to the Graft per >> default. >> >> Grafts can be used in an similar way as Traits can be used. Thus, the >> example >> from the introduction can be optimized with Grafts as well. >> :: >> >> > class ezcReflectionReturnInfo { >> function getReturnType() { /*1*/ } >> function getReturnDescription() { /*2*/ } >> } >> >> class ezcReflectionMethod extends ReflectionMethod { >> use ezcReflectionReturnInfo { >> public getReturnType(); >> public getReturnDescription(); >> } >> /* ... */ >> } >> >> class ezcReflectionFunction extends ReflectionFunction { >> use ezcReflectionReturnInfo { >> public getReturnType(); >> public getReturnDescription(); >> } >> /* ... */ >> } >> ?> >> >> Since, everything is local to the Graft per default, the methods >> need to be >> enumerated to be accessible in the class and its interface. >> >> Grafting Classes >> ---------------- >> >> A Graft is a class itself and can be instantiated at will. Compared >> to a >> normal >> class, there are no restrictions, it can use state and it can use >> everything else allowed for a class i.e. it is a normal class. >> >> The following class is an example for a simple counter:: >> >> > class Counter { >> private $cnt = 0; >> public function inc() { >> $this->cnt++; >> } >> public function reset() { >> $this->cnt = -1; >> $this->inc(); >> } >> } >> ?> >> >> The ``reset()`` is defined a bit strange, because it uses the ``inc >> ()`` >> function to set the counter value to zero. This is to illustrate the >> semantics >> of a Graft inside another class. This counter can be used totally >> on its >> own. >> Thus, ``$c = new Counter(); $c->inc();`` is valid code and will >> work as >> expected. >> >> Another example class might be a database helper class to >> initialize and >> reset >> a connection:: >> >> > class DB { >> private $db; >> public function connect() { >> $this->db = new FooDB('param'); >> } >> public function reset() { >> $this->db->flush(); >> unset($this->db); >> } >> public function doFoo(){echo 'foo';} >> } >> >> Nothing special in this class. The ``connect()`` function uses some >> Foo >> database abstraction layer to initialize the connection and ``reset >> ()`` will >> issue a flush operation on the database object and unsets it >> afterwards. >> >> Compose A Class From Classes >> """""""""""""""""""""""""""" >> >> Inheritance is one of the most misunderstood relationships in >> object-oriented >> programming. It is often abused to achieve code reuse, but >> originally it was >> meant to describe the relationship between classes from a >> conceptual view. >> Thus, apples and oranges might be characterized as subclasses of a >> class >> fruit >> and could be subclassed themselves into classes for specific >> variants. >> >> In a web application where you for instance would like to build a >> page with >> a >> language allowing multiple inheritance you could start to model a >> ``MyPage`` >> class as subclass of ``Counter`` and ``DB``, since you like to >> derive the >> behavior of both of them. But from the conceptual view it is not >> clear why >> MyPage is a counter or a database. Both relationships are implied >> by the >> ``instanceof`` >> operation. Technically, this might have its values, but still it >> sounds >> strange. >> >> Thus, in single inheritance languages, *delegation* i.e. >> *forwarding* is >> used >> instead. The ``MyPage`` class provides the necessary interfaces for >> ``Counter`` >> and ``DB`` but instead of implementing it itself, it forwards the >> method >> call >> to another object:: >> >> > class MyPage { >> private $cnt; >> private $db; >> /* ... */ >> public function inc() { $cnt->inc(); } >> public connect() { $db->connect(); } >> /* ... */ >> } >> ?> >> >> This approach is very common and has its merits. Unfortunately, it >> requires >> explicit code to implement forwarding and object injection or >> creation which >> is >> tedious. In complex cases this might even cause a broken >> encapsulation, >> since >> it might be necessary to forward data to another object which >> should be >> private >> to the calling object. >> >> Grafts are designed to overcome this situation and provide similar >> opportunities for reuse like Traits do. The following code >> demonstrates how >> Grafts can be used to compose the ``MyPage`` class from the two other >> classes:: >> >> > class MyPage { >> use Counter { >> public incCnt() from inc(); >> public resetCnt() from reset(); >> } >> use DB { >> public connect(); >> public reset(); >> } >> public function inc() { >> /* next page */ >> } >> } >> ?> >> >> The example above shows ``MyPage`` using classes ``Counter`` and >> ``DB``, to >> graft their functionality into it. Since, all methods from a >> grafted class >> are >> hidden from the grafting class by default, the methods to be used >> in the >> grafting class or from another class need to be named explicitly. >> With this approach, conflicts are avoided upfront. In case of >> methods with >> the >> same name in a graft and another graft or the class itself, it is >> possible >> to >> make a method available by another name. In the given example the >> method >> ``reset()`` is made available by the name ``resetCnt()``. This >> alias does >> not >> influence the inner working of the ``Counter`` class in anyway. Thus, >> recursion >> inside of ``Counter::reset()`` would still work like expected and >> the call >> to >> ``incCnt()`` results in an invocation of ``Counter::inc()``. This >> would be >> not >> true for Traits as explained earlier. >> >> With this attributes of Grafts in mind, the following example shows >> the >> results >> of the execution of methods in the context of ``MyPage``:: >> >> > $page = new MyPage(); >> $page->connect(); >> $page->incCnt(); // Counter::$cnt == 1 >> $page->resetCnt(); // Counter::$cnt == 0 >> $page->inc(); // goto next page >> $page->doFoo(); // FATAL ERROR >> ?> >> >> The call to ``connect()`` is forwarded as expected to ``DB::connect >> ()``, >> ``incCnt()`` results in a call to ``Counter::inc()`` as already >> mentioned, >> the >> call to ``inc()`` will invoke the ``MyPage::inc()`` method defined >> directly >> in >> the class. The call to ``doFoo()`` results in an error, since the >> method >> defined in ``DB`` has not been made available in ``MyPage`` >> explicitly. >> >> Interaction of Grafts with Grafted Classes >> """""""""""""""""""""""""""""""""""""""""" >> >> In class-based languages like PHP, the notion of abstract classes was >> introduced, to be able to define a unit of reuse in a partial >> manner and to >> be able to refine it in a more concrete context. >> >> This notion can be used combined with grafting as well. Thus, >> abstract >> methods >> can be fulfilled by a graft or from the grafting class. >> >> The following example shows how a class can be used by grafting to >> fulfill a >> required abstract method:: >> >> > abstract class Base { >> abstract public function foo(); >> } >> >> class MyGraft { >> public function foo() { echo 'foo'; } >> } >> >> class MyGraftedClass extends Base { >> use MyGraft { >> public foo(); >> } >> } >> >> $o = new MyGraftedClass(); >> $o->foo(); // echos 'foo' >> ?> >> >> In addition, this notion can also be utilized to provide methods to >> a graft, >> which states a requirement by an abstract class. This case is shown >> in the >> code below:: >> >> > abstract class MyGraft { >> abstract public function foo(); >> public function bar() { >> $this->foo(); >> } >> } >> >> class MyGraftedClass { >> public function foo() { echo 'foo'; } >> use MyGraft { >> public bar(); >> } >> } >> >> $o = new MyGraftedClass(); >> $o->bar(); // echos 'foo' >> ?> >> >> For properties and methods not explicitly required, it is not >> possible to >> provide something similar, since this would lead to ambiguity. The >> reasons >> are described in the discussion section. >> >> Grafting of the Same Class Multiple Times >> """"""""""""""""""""""""""""""""""""""""" >> >> Since, different grafts are separated from each other, it is >> possible to >> graft >> the same class multiple times into one class:: >> >> > class MyGraft { >> public function foo() { echo 'foo'; } >> } >> class MyGraftedClass { >> use MyGraft { >> public foo(); >> } >> use MyGraft { >> public bar() from foo(); >> } >> } >> $o = new MyGraftedClass(); >> $o->foo(); // echos 'foo' >> $o->bar(); // echos 'foo' >> ?> >> >> This might not be useful for all classes, but could be used to >> graft data >> structures like lists or maps into a class which are used for >> multiple >> purposes. >> >> Restrictions and Initialization of Grafts >> """"""""""""""""""""""""""""""""""""""""" >> >> Actually an instantiated graft is nothing else but an object. Since >> grafts >> are >> full-fledged classes themselves, they could use typical >> constructors to >> initialize resources or prepare their inner state for further >> usage. In the >> current form of this proposal, the notion of initializing grafts is >> not >> supported, thus, only classes with a standard constructor without >> arguments >> can >> be used to be grafted into another class. >> >> Another restriction is that classes can not graft themselves and more >> generally, grafting is not allowed to cause recursion in any way. >> Since, it >> is >> not meant to be evaluated lazily, it would case an infinite >> recursion which >> needs to be prevented. >> >> Implementation Idea >> """"""""""""""""""" >> >> Eventually, the implementation seems only to be feasible by >> maintaining the >> notion of separate objects for classes grafted into another class. >> Thus, for >> each graft in a class, a normal object is instantiated. This >> approach will >> ease >> the implementation and will reduce modification in method lookup >> and the >> Zend >> Engine in general. >> >> Methods propagated to the grafting class will be generated like usual >> methods >> implemented by hand. Thus, they will contain the opcodes to forward >> the >> method >> call to the grafted object. In reverse, it would be the same for >> methods >> introduced by abstract methods satisfied by the grafting class. Those >> methods >> will call the method provided by the grafting class. >> >> The most visible problem in this approach will be the handling of >> object >> identity of grafted objects. Imagine the following code:: >> >> > class MyGraft { >> public function returnThis() { >> return $this; >> } >> } >> class MyGraftedClass { >> use MyGraft { >> public returnThis(); >> } >> } >> $o = new MyGraftedClass(); >> var_dump($o->returnThis()); >> ?> >> >> The result returned should be definitely equal to ``$o`` i.e. ``$o >> === >> $o->returnThis()``. The reason for this requirement is to preserve >> encapsulation and hide implementation details from all clients of >> ``MyGraftedClass``. To achieve this property some kind of context >> dependent >> treatment of the ``$this`` lookup has to be implemented or some >> kind of data >> flow >> analysis will have to be done. Neither of them seem to be easy to >> achieve >> with >> respect to the fact, that a grafted class cold hold its own `` >> $this`` value >> in >> some property, for whatever reason. >> >> The other way around would be to open up the notion of grafts and add >> mechanisms to inject instances of grafts into a class while object >> construction. By the way, this would allow more fancy dependency >> injection >> and gives a reason to think about grafts not only as implementation >> details, but an interface related i.e. *client visible* >> characteristic. >> But, this notion is currently not part of this proposal. >> >> Grafts Semantics Summarized >> --------------------------- >> >> 1. A Graft is a normal class composed into another class. >> 2. The encapsulation of grafts is preserved completely. >> 3. All methods available as part of the grafting class have to be >> enumerated >> explicitly and can have a different name than the method in the >> grafted >> class, without breaking inner coherence. >> 4. Grafts can interact with grafting classes via abstract methods. >> 5. Methods introduced by a Graft can be used to fulfill abstract >> methods. >> 6. Classes can be grafted into a class multiple times. >> 7. Classes to be used as Grafts are not allowed to use constructors >> with >> arguments. >> >> Traits vs. Grafts >> ================= >> >> This section gives a basic overview about the differences of both >> concepts >> and >> discusses benefits and drawbacks. >> >> *Traits* are entities of behavioral reuse. Thus, they provide a >> lightweight >> way >> to compose methods into classes. They are highly composable >> themselves, not >> hindered by a strong notion of encapsulation and abandon state for >> the sake >> of >> simplicity. >> >> *Grafts* are classes composed into other classes to achieve reuse of >> full-fledged classes. It introduces a stronger notion of >> composition of >> classes >> than typical delegation/forwarding does for OOP languages. >> >> +------------------------+-----------------------------+ >> | Traits | Grafts | >> +========================+=============================+ >> | - stateless | - complete classes | >> | - can "break" | - robust encapsulation | >> | - notation is DRY | - some overhead in notation | >> | - flexible composition | - convenient composition | >> | of behavior | of classes | >> | - flattenable, | - language supported | >> | no runtime impact | delegation/forwarding | >> +------------------------+-----------------------------+ >> >> This proposal does only suggest Traits without state to avoid the >> introduction >> of any notion of Traits at runtime. The key idea of Traits is that >> they >> provide >> method implementations which are flattened into a class. Thus, all >> state is >> defined in the class and Traits method have to rely on PHPs dynamic >> nature >> or >> have to use abstract method definitions to require getters and >> setters. >> Since Grafts are full-fledged classes, they have state and handle >> it as >> expected for distinct objects. >> >> One point of criticism from the community on the notion of Traits >> is that >> the >> methods provided by Traits are not reliable connected. This leads >> to missing >> features like recursion for methods introduced by aliases. From the >> perspective >> of composition, this is desired, since it provides additional >> opportunities >> for >> combination of behavior. Traits are not meant to provide >> encapsulation on >> the >> level of classes, but flexibility. >> Grafts do avoid this problem by its nature of being classes, but >> this comes >> for >> the cost of flexibility. Grafts can not combined with another. They >> are >> completely separated entities which only can be combined by abstract >> methods. >> >> The notion of Traits avoids any impact on the runtime. For Grafts >> this is >> not >> feasible. They need to maintain strong encapsulation which is >> achieved in an >> consistent way only by preserving the notion of internal objects. >> Thus, >> there >> is overhead in memory consumption for the objects and runtime cost >> for >> forwarding method calls compared to Traits. >> >> Another point where Grafts might be improved (see syntax proposals) >> is the >> syntax for using a class. To avoid conflicts methods are private to >> a graft >> per >> default and thus, need to be enumerated explicitly. Compared to >> Traits this >> is >> not DRY and implies a little overhead in code, but obviously less >> overhead >> than >> typical forwarding cases. With respect to Traits, research has >> shown that >> conflicts occur only rarely and explicit handling of it is much less >> overhead >> and brings more power than trying to solve it implicitly. >> >> Discussions >> =========== >> >> For the original Traits proposal a lot discussion has already taken >> place. >> The RFC_ >> summarizes the most important ones. Eventually, it resulted in an >> additional >> RFC-NBT_ proposing non-breakable Traits, which led finally to this >> proposal. >> The first thread on was started on the `mailing list` >> (http://news.php.net/php.internals/35562) back in February 2008. >> >> Grafts >> ------ >> >> PHP as a dynamic language would give the opportunity to work with >> this >> grafts functionality more flexible, even without abstract method >> definitions, >> but this would be very unintuitive since the graft does not know it >> is using >> >> methods or properties from the grafted class. On the one hand this >> would >> even >> break the notion of encapsulation and on the other hand, it would >> cause >> trouble >> at least for properties because here it might be that the different >> order of >> method >> execution results in unintended different results. Imagine >> something simple >> like:: >> >> > class MyGraft { >> public function get() { return $this->value; } >> public function set($value) { $this->value = $value; } >> } >> ?> >> >> Grafted into a class it would depend on the behavior of the grafted >> class, >> whether there is a property named value and thus the encapsulation is >> broken. >> Thus, it would be ambiguous to allow a forwarding from within a >> grafted >> class >> to its grafting class for non-defined properties and methods. >> >> Alternative Syntax Proposals >> ---------------------------- >> >> Different keywords and alternative syntaxes for traits have been >> already >> discussed on the `mailing list` and are documented more detailed in >> the >> original RFC_. >> >> Some important proposals and new additional proposals are listed >> below for >> traits as well as grafts. >> >> Traits >> """""" >> >> Assignment instead of ``instead``: >> >> Keywords are a rare resource in any language. Thus, new keywords >> should be >> introduced carefully. To avoid the ``instead`` keyword, a notion of >> assignment >> was proposed. On the first look, it seems even to avoid an >> impression of >> renaming:: >> >> > 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; >> } >> } >> ?> >> >> Grafts >> """""" >> >> Grafts use wildcard: >> >> The proposed Grafts syntax is not DRY and all methods names to be >> made >> available need to be enumerated. To avoid this enumeration, a >> syntax with a >> wildcard was proposed:: >> >> > class MyGraftingClass { >> use MyGraftClass *; >> } >> ?> >> >> Links and Literature >> ==================== >> >> .. _RFC: http://www.stefan-marr.de/artikel/rfc-traits-for-php.html >> .. _RFC-NBT: http://wiki.php.net/rfc/nonbreakabletraits >> .. _agrarian cultivation: >> http://www.robinsonlibrary.com/agriculture/plant/propagation/grafting.htm >> .. _mailing list: http://marc.info/?l=php-internals&m=120336491008719 >> >> As already mentioned, Traits is not a totally new concept, but the >> semantics >> used in this proposal has been fully defined at first in 2003. For >> scientific >> information and papers about Traits >> http://www.iam.unibe.ch/~scg/Research/Traits/ >> is a good starting point. Since it isn't a purely academic >> concepts, there >> are >> already languages supporting Traits out there. Squeak, Perl6, >> Scala, Slate, >> Fortress and even for C#/Rotor implementation are available. >> >> A detailed technical report has been published at >> http://www.iam.unibe.ch/~scg/Archive/Papers/Duca06bTOPLASTraits.pdf >> It explains Traits and gives some formal proves about the soundness >> of >> Traits, too. >> >> Last but not least, in this PhD thesis >> http://www.iam.unibe.ch/~scg/Archive/PhD/schaerli-phd.pdf >> two case studies have been publish illustrating the benefits Traits >> are >> providing. >> >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: http://www.php.net/unsub.php >> > > DJ Suicide Dive > dj@ifnotwhynot.me > > > > Lukas Kahwe Smith > mls@pooteeweet.org > > > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > -- Stefan Marr Software Languages Lab Former Programming Technology Lab Vrije Universiteit Brussel Pleinlaan 2 / B-1050 Brussels / Belgium http://prog.vub.ac.be/~smarr Phone: +32 2 629 3956 Fax: +32 2 629 3525