Re: RFC: Traits for PHP
| From: | Marcus Boerger | Date: | Tue, 19 Feb 2008 16:58:37 +0000 |
| Subject: | Re: RFC: Traits for PHP | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-35599@lists.php.net to get a copy of this message | ||
Hello php,
looks good to me. See more detailed thoughts in separate mail resonses.
The biggest issue I see is finding a syntax everyone likes.
Personally I like everything but one tiny piece, that is you do '!method'
to ignore a method from a trait. Since renaming happens in a php array like
style I would prefer to have that approach apply for ignoring methods as
well. The way to do that imo is 'method=>false' or 'method=>NULL' which
both
should be obvious to PHP programmers that heard about Traits.
Other than that I'd even appriciate it, if we could get this for 5.3. I mean
come on guys it is ready and a very nice solution to the code reuse problem.
marcus
Monday, February 18, 2008, 8:27:00 PM, you wrote:
> Hi,
> during last six months I've studied a language construct called Traits.
> It is a construct to allow fine-grained code reuse and in my opinon
> this would be a nice feature for PHP, which I did like to propose here.
> The following RFC deals with the questions what Traits are, how they are
> used, why they are usefull and how they do look like in PHP.
> A patch implementing this new language construct is available, too.
> Thank you for your attention and I'm looking forward to hear your comments
> :)
> Kind Regards
> Stefan
> Request for Comments: Traits for PHP
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> :HTML:
> http://www.stefan-marr.de/artikel/rfc-traits-for-php.html
> ... contents::
> This RFC will discuss at first the motivation for Traits describing the
> rationals
> and presenting a short real world use case. The main part will describe the
> concept of Traits in detail using the syntax for Traits implemented in a
> patch
> which is part of this proposal. In the end, the URL of the patch and
> additional resources about Traits are given.
> Introduction
> ============
> *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 enabeling 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 newer versions of languages
> such as Perl 6, Squeak, Scala, Slate and Fortress.
> Traits have also been ported to Java and C#.
> Why do we need Traits?
> ----------------------
> 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++.
> Traits have been invented to avoid those problems, too. They enable designer
> 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.
> Traits: A Mechanism for Fine-grained Reuse
> ==========================================
> A Trait is a unit of reuse much like 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.
> 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 some situations it
> would be possible to add a common base class, but in this case it is
> impossible, because the extended classes are not under our control, i.e.,
> they
> are implemented in third party code or even in C, like it is the case here.
> ::
> <?php
> class ezcReflectionMethod extends ReflectionMethod {
> /* ... */
> function getReturnType() { /*1*/ }
> function getReturnDescription() { /*2*/ }
> /* ... */
> }
> class ezcReflectionFunction extends ReflectionFunction {
> /* ... */
> function getReturnType() { /*1*/ }
> function getReturnDescription() { /*2*/ }
> /* ... */
> }
> ?>
> With Traits it is possible to refactor this redundant code out.
> ::
> <?php
> 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 on more advanced techniques and describe how
> the
> current implementation of Traits for PHP works.
> 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.
> 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.
> ::
> <?php
> 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.
> ::
> <?php
> 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.
> ::
> <?php
> trait Hello {
> public function sayHello() {
> echo 'Hello ';
> }
> }
> trait World {
> public function sayWorld() {
> echo ' World';
> }
> }
>
> class MyHelloWorld {
> use Hello;
> use World;
> public function sayExclamationMark() {
> echo '!';
> }
> }
>
> $o = new MyHelloWorld();
> $o->sayHello();
> $o->sayWorld();
> $o->sayExclamationMark();
> // Results eventually in: Hello World!
> Conflict Resolution
>
> """""""""""""""""""
> But now a problem will occur, if different Traits provide methods with the
> same name.
> ::
> <?php
> 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
> aren't solved implicitly by any kind of precedence. Instead, to avoid
> implicit
> complexity, the developer has full control over class composition.
> ::
> <?php
> class Talker {
> use A;
> use B;
> }
> ?>
>
> In case of the above definition of `Talker`, PHP will show a
> notice 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.
> ::
> <?php
> class Talker {
> use A { !smallTalk }
> use B { !bigTalk }
> }
> ?>
> This definition will result in the exclusion of `smallTalk()`
> from the
> Trait A
> and `bigTalk()` from Trait B. Therefore, the resulting class
> Talker would
> echo `'b' for
> smallTalk() and
> 'A' for
> bigTalk().`
> But simple exclusion of methods is not the best choice for all situations.
> ::
> <?php
> class Talker {
> use A { !smallTalk }
> use B { !bigTalk, talk => bigTalk }
> }
> ?>
>
> Beside the exclusion an alias operation is available, too. This alias
> operation, notated like a `key => value` for arrays even has
> a similar
> semantics like the array notation. The definition `talk =>
> bigTalk`
> lets the new name `talk refer to the method
> body of bigTalk`
> of the Trait B. The resulting `Talker` class will consist of
> following
> three methods:
> * `bigTalk() { echo 'A'; }`
> * `smallTalk() { echo 'b'; }`
> * `talk() { echo 'B'; }`
> Since the alias operation adds a new name to an existing method body, the
> `bigTalk` method still has to be excluded. Otherwise, PHP would
> print
> a notice that two methods from Traits have a conflict and are excluded.
> Aliasing 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::
> <?php
> trait Hello {
> public function sayHello() {
> echo 'Hello ';
> }
> }
> trait World {
> public function sayWorld() {
> echo 'World!';
> }
> }
> trait HelloWorld {
> use Hello;
> use 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.
> 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 notice is generated and the conflicting methods
> are excluded.
> 4. Specific methods can be excluded from a composition to handle conflicts.
> 5. Aliases can be defined for methods to enable reuse of conflicting
> methods.
> 6. Traits can be composed from Traits.
> 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 and Interfaces
> -------------------------
> 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.
> ::
> <?php
> trait HelloWorld {
> public function sayHello() {
> echo 'Hello World!';
> }
> }
> class MyClass1 {
> use HelloWorld { protected sayHello }
> }
> class MyClass2 {
> use HelloWorld { private doHelloWorld => sayHello }
> }
> ?>
> The abstract and final modifiers are supported, too. Abstract methods in
> Traits are commonly used to define requirements a Trait needs to have
> implemented by a class. The static modifier is not supported, because it
> would
> change the methods semantics and references to `$this` would
> break.
> Another important feature of PHP is the support of interfaces. A often used
> metaphor to describe Traits is *Traits are interfaces with implementation*.
> Traits can be utilized to provide the implementation for a specific
> interface
> and since an interface is a guarantee that some methods are available it
> fits
> in the concept of Traits which provides those methods very well.
> To underpin this relationship, it is possible to declare that a Trait
> implements an interface like this::
> <?php
> interface IHello {
> public function sayHello();
> }
>
> trait SayHello implements IHello {
> public function sayHello() {
> echo 'Hello World!';
> }
> }
> class MyHelloWorld {
> use SayHello;
> }
>
> $o = new MyHelloWorld();
> var_dump($o instanceof IHello); // bool(true)
> If a Trait implements an interface, this definition is propagated to the
> class
> using the Trait. Therefore, it is possible to provide implementations for an
> interface
> and reuse them in different classes.
> Proposal and Patch
> ==================
> This Request for Comments proposes a new language feature for PHP named
> Traits.
> Traits are a nice approach to enhance the capabilities to design conceptual
> consistent class hierarchies and avoid code duplication.
> The patch against the PHP_5_2 branch is available at
> http://toolslave.net/snapshots/traits/traits.patch
> The written test cases are located here:
> http://toolslave.net/snapshots/traits/traits-tests.zip
> Additionally, the SVN repo used for developing this patch is located at
>
> https://instantsvc.svn.sourceforge.net/svnroot/instantsvc/branches/php-exten
> sion/traits-php/
> Alternative Syntax Proposals
> ----------------------------
> This section collects proposals for alternative Traits syntaxes.
> Traits Use Definition in the Class Header
>
> """""""""""""""""""""""""""""""""""""""""
> Instead of declaring the Trait composition in the class body, it could be
> defined in the class prologue like this::
>
> <?php
> trait Hello {
> public function sayHello() {}
> }
> class MyHelloWorld extends BaseClass
> uses Hello (hello => sayHello, !sayHello)
> {
> public function foo() {}
> }
> ?>
> The drawback of this notation is the implied notation of Traits as some kind
> of
> a type changing construct. Since they do not influence the type as their
> major
> feature, this notion would be misleading. Furthermore, this notation seams
> to
> have readability problems. Complex compositions are not as clearly arranged
> as
> they are with the *In-Body* notation.
> A patch implementing this notation is available at:
> http://toolslave.net/snapshots/traits/traits-head-syntax.patch
> More about Traits
> -----------------
> 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.
Best regards,
Marcus