On 18/02/2008, [EMAIL PROTECTED] <[EMAIL PROTECTED]> 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.

>From a userland perspective (i.e. not knowing the internals impact),
this looks like a great feature which treats the userland developer
with a bit of respect.

It allows me to take a class and add some addition functionality to it
without subclassing.
Conflicts are MY responsibility to solve.
No magic.

>From a userland developer, +1.

A question (and again, no idea on feasibility, internals, etc.).

The above proposal covers adding/overriding internal methods with
external ones. Is it possible to also include additional properties?

If not, then you would have to override the constructor method (this
could be demoted to documentation rather than code then I suppose).

-- 
-----
Richard Quadling
Zend Certified Engineer : http://zend.com/zce.php?c=ZEND002498&r=213474731
"Standing on the shoulders of some very clever giants!"

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to