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 <php.at.stefan-marr.de>
: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.
::
<?php
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.
::
<?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 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.
::
<?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, 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.
::
<?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
are not 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, 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.
::
<?php
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.
::
<?php
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::
<?php
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.
::
<?php
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.
::
<?php
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.
::
<?php
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::
<?php
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::
<?php
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::
<?php
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::
<?php
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``::
<?php
$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::
<?php
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::
<?php
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::
<?php
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::
<?php
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::
<?php
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::
<?php
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::
<?php
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.
Hej everybody,
I had a chat with Stefan about his Traits/Grafts RFC and he suggested we
should rather continue a discussion here.
I really liked to see the Grafts proposal. In traits and regular
inheritance the implementation code of several entities is somehow mixed
and as a result one entities code can break another entities code. The
great thing about Grafts is that the implementation is not mixed. Grafts
are completely independent entities with their own state. I think in
many cases where inheritance or traits seem tempting, complete
encapsulation is actually the cleaner solution.
The Grafts proposal, however, suffered a little from being born out of
traits, I think. Something similar to Grafts is already possible in
current php, but it is not very beautiful. If we start from there
however, Grafts could become very helpful syntactic sugar. Let's look at
current PHP first:
class Counter {
private $cnt = 0;
public function inc() {
$this->cnt++;
}
public function reset()
{
$this->cnt = -1;
$this->inc();
}
}
class TakeANumberForTheQueue{
, so this is one , but I think it could be even better when it was much
closer to current PHP than suggested in the RFC. Let me explain what I
mean. Graft were born out of the idea of traits. Traits are a variant of
inheritance and a trade-off between single and multiple inheritance with
the goal to reduce code duplication. The problem with inheritance is
that code can break because code from different classes (or traits) is
mixed together
interfering with Either kind of inheritance can be very tempting to be
used to reuse functionality.
implement code reuse. Code reuse is tempting but often enough it is not
really necessary to reuse the
comes from the wrong direction and didn't go far enough.
Traits conquer the lacks of single inheritance without giving the full
power of multiple inheritance. Traits are still sort of inheritance.
When a class uses a trait the trait's code can access class
implementation details and vice-vera. This might come in handy at some
point, however I personally lack a good use case for now. Can somebody
provide a sensible use case for traits?
A Problem with code inheritance (single, traits, whatever) is that the
possibility to use inheritance seduces to use it even when it is not
necessarily needed. Stefan's "Counter" example is a good one.
What I find even more promising than Traits are Grafts.
I know Grafts where born out of the traits idea, but I want to try to
put them idea a little forward and separate the from the traits idea
even stronger than Stefan already did.
Why do we want Grafts?
Because inheritance can be a problem
Gnaaa. Hit the submit button too early. Excuse the rubbish. I will post
the correct mail in a moment.
Christopher
Hej everybody,
I had a chat with Stefan about his Traits/Grafts RFC and he suggested we
should rather continue a discussion here.
I really liked to see the Grafts proposal. In traits and regular
inheritance the implementation code of several entities is somehow mixed
and as a result one entities code can break another entities code. The
great thing about Grafts is that the implementation is not mixed. Grafts
are completely encapsulated entities with their own state. I think in
many cases where inheritance or traits seem tempting, complete
encapsulation is actually the cleaner solution.
The Grafts proposal, however, suffered a little from being born out of
traits, I think. Something similar to Grafts is already possible in
current php, but it is not very beautiful. If we start from there
however, Grafts could become very helpful syntactic sugar. Let's look at
current PHP first:
class Counter {
private $cnt = 0;
public function inc() {
$this->cnt++;
return $this;
}
public function reset()
{
$this->cnt = -1;
$this->inc();
return $this;
}
public function current()
{
return $this->cnt;
return $this;
}
}
class QueueTicketPrinter{
private $counter;
public __construct(){
$this->counter = new Counter();
}
public function takeNumber(){
print 'your number is ".$this->counter->current();
$this->counter->inc();
}
public function current()
{
$this->counter->current();
}
public function reset()
{
$this->counter->reset();
}
}
This is a lot of code in QueueTicketPrinter for that it mostly reuses
Counter. Grafts as syntactic sugar could make it look as short as:
class QueueTicketPrinter{
use Counter as private $counter{
public current()
;
public reset()
;
}
public function takeNumber(){
print 'your number is ".$this->counter->current();
$this->counter->inc();
}
}
However a problem remains. The methods of counter return $this (aka
implement a fluent interface). One idea that came to my mind to solve
this problem: PHP could provide a keyword fluent that replaces a methods
return value to form a fluent interface i.e. return $this:
class QueueTicketPrinter{
use Counter as private $counter{
public fluent current()
;
public fluent reset()
;
}
public function takeNumber(){
print 'your number is ".$this->counter->current();
$this->counter->inc();
}
}
The keyword fluent ignores whatever value the Counter function may
return and returns an the instance of QueueTicketPrinter instead.
Finally, can somebody provide a sensible use case for traits, that
cannot be solved with Grafts? I am sure there is, but I am currently
lacking one.
Cheers
Christopher
P.S. Hope that stupid post before does not make me seem too much like it ;).
Hi,
Christopher Vogt schrieb:
Hej everybody,
I really liked to see the Grafts proposal.
Well, I'm still in love with the more powerful (because they are
interweaveable(breakable)) Traits ;)
The Grafts proposal, however, suffered a little from being born out of
traits, I think. Something similar to Grafts is already possible in
current php, but it is not very beautiful. If we start from there
however, Grafts could become very helpful syntactic sugar.
Actually I think this could be a problem for the conciseness of the
language. Introducing syntactic sugar for special situations has to be
done carefully. Nevertheless, from my perspective forwarding (aka
delegation) is a very common concept in OOP which would be worth to be
supported by a special syntax.
class QueueTicketPrinter{
use Counter as private $counter{
publiccurrent()
;
publicreset()
;
}
public function takeNumber(){
print 'your number is ".$this->counter->current();
$this->counter->inc();
}
}
I like this idea, but there are still some problems to be solved. The
major problem with grafts is still the initialization of objects. What
to do with constructors which require parameter?
An other issue I see is the question of introducing to much additional
syntax. It could be reduced to something like:
private $counter : Counter [use] { //with or without use?
public current()
;
public reset()
;
}
But now anonymous grafts aren't possible anymore and actually, Grafts
had been design without an identity own their own. On the other hand,
this notation could introduce the possibility for advanced initialization:
class QueueTicketPrinter{
private $cntInitValue;
public function __construct($cntValue) {
$this->cntInitValue = $cntValue;
}
private $counter : Counter(cntInitValue) {
public `current()`;
public `reset()`;
}
public function takeNumber(){
print 'your number is ".$this->counter->current();
$this->counter->inc();
}
}
Finally, can somebody provide a sensible use case for traits, that
cannot be solved with Grafts? I am sure there is, but I am currently
lacking one.
Every time you just like to compose a class from different kinds of
behaviors Traits are superior. The use cases discussed in literature are
class hierarchies for collection classes and streams. For both
hierarchies, the interweaving of methods from different traits is a
helpful property to build all variations of semantics.
Kind Regards
Stefan
Hej,
I really liked to see the Grafts proposal.
Well, I'm still in love with the more powerful (because they are
interweaveable(breakable)) Traits ;)
I don't think there has to be a choice between Grafts and Traits. They
serve different purposes and I don't see a reason they shouldn't
co-exit. Traits implement code reuse. Code is merged. This is very
powerful but can break code. Grafts implement delegation. This is less
powerful, but can't break, which makes it preferable, whenever possible.
Actually I think this could be a problem for the conciseness of the
language. Introducing syntactic sugar for special situations has to be
done carefully.
In my eyes (well-chosen) syntactic sugar contributes a lot to language
conciseness. And every language decision should be done carefully, right?
from my perspective forwarding (aka
delegation) is a very common concept in OOP which would be worth to be
supported by a special syntax.
I strongly think so.
I like this idea, but there are still some problems to be solved. The
major problem with grafts is still the initialization of objects. What
to do with constructors which require parameter?
Good point. My new suggestion at the end covers it.
An other issue I see is the question of introducing to much additional
syntax. It could be reduced to something like:private $counter : Counter [use] { //with or without use?
publiccurrent()
;
publicreset()
;
}
I don't see the above having less syntax than my suggestion. "as" or ":"
is both syntax. I doubt, I would rather stick to a keyword already in
the language in similar semantics. For Comparison, my suggestion was:
use Counter as private $counter{
publiccurrent()
;
publicreset()
;
}
But now anonymous grafts aren't possible anymore and actually, Grafts
had been design without an identity own their own.
Yes and I don't think anonymous Grafts would be a good idea. They would
make access to grafts complicated and significantly limited including
valid use cases. Being explicit instead of implicit helps here in my eyes.
On the other hand,
this notation could introduce the possibility for advanced initialization:private $cntInitValue; public function __construct($cntValue) { $this->cntInitValue = $cntValue; } private $counter : Counter(cntInitValue) { public `current()`; public `reset()`; }
I don't think that's a good idea. I admit it's very close to my
suggestion, but now it reached a point, where it does something very
familiar, namely creating an new object, using a very unfamiliar way and
also new syntax. So I make a new suggestion that puts everything much
closer to present PHP:
class QueueTicketPrinter{
private $counter {
publiccurrent()
;
public restart() fromreset()
;
};public function __construct($cntValue) { $this->counter = new Counter( $cntValue ); } public function takeNumber(){ print 'your number is ".$this->current(); $this->counter->inc(); }
}
And two syntax variants:
Variant 1:
private $counter use { public `current()`; `reset()` as public restart(); };
Variant 2:
private $counter delegate { public `current()`; public restart() to `reset()`; };
This suggestion requires a new runtime Error/Exception for cases when a
method of QueueTicketPrinter is called that is delegated to a method
that $counter does not implement.
Every time you just like to compose a class from different kinds of
behaviors Traits are superior.
More powerful yes, superior no. Delegation and inheritance (counting
traits as a variant of inheritance) both allow functionality re-use but
both also resemble a trade-off between flexibility and "breakability".
Inheritance is more flexible, but can easily break, especially when
inheritance structures grow. Delegation is less flexible, but can't
break (from reuse), even in large structures. So both are justified.
Best regards
Christopher
P.S. I should mention that I do not have any insight of the actual
implementation of PHP. So if I suggest something insensibly hard to
implement, just shout :).
Hello Stefan,
Sunday, November 16, 2008, 4:36:18 PM, you wrote:
Hi,
Christopher Vogt schrieb:
Hej everybody,
I really liked to see the Grafts proposal.
Well, I'm still in love with the more powerful (because they are
interweaveable(breakable)) Traits ;)
The Grafts proposal, however, suffered a little from being born out of
traits, I think. Something similar to Grafts is already possible in
current php, but it is not very beautiful. If we start from there
however, Grafts could become very helpful syntactic sugar.
Actually I think this could be a problem for the conciseness of the
language. Introducing syntactic sugar for special situations has to be
done carefully. Nevertheless, from my perspective forwarding (aka
delegation) is a very common concept in OOP which would be worth to be
supported by a special syntax.
class QueueTicketPrinter{
use Counter as private $counter{
publiccurrent()
;
publicreset()
;
}
public function takeNumber(){
print 'your number is ".$this->counter->current();
$this->counter->inc();
}
}
I like this idea, but there are still some problems to be solved. The
major problem with grafts is still the initialization of objects. What
to do with constructors which require parameter?
An other issue I see is the question of introducing to much additional
syntax. It could be reduced to something like:
private $counter : Counter [use] { //with or without use?
publiccurrent()
;
publicreset()
;
}
But now anonymous grafts aren't possible anymore and actually, Grafts
had been design without an identity own their own. On the other hand,
this notation could introduce the possibility for advanced initialization:
class QueueTicketPrinter{
private $cntInitValue;
public function __construct($cntValue) {
$this->cntInitValue = $cntValue;
}
private $counter : Counter(cntInitValue) { public `current()`; public `reset()`; }
public function takeNumber(){ print 'your number is ".$this->counter->current(); $this->counter->inc(); }
}
This is hardly more than delegates. Actually the only thing more you get is
hiding stuff which is incompatible to the thing you loose at the same time.
That is your grafts do not support interface delegation. So My preference
is being able to actually do that (and mixins/traits do). Example:
Interface Counter {
function current()
;
function reset()
;
}
Class CounterImpl implements Counter {
private $cnt = 0;
function current()
{ return $this->cnt; }
function reset()
{ $this->cnt = 0; }
}
Class CounterUser implements Counter {
delegate Counter $counter;
function __construct() {
$this->counter = new CounterImpl;
}
}
The keyword delegate makes sure $counter is a member that must implement
Counter. Assigning any other value would result in an exception or other
error. To make it easier the delegate declaration wcould contain the
initialization already and turn the delegate member into a read only
member.
deletegate Counter $counter = new CounterImpl;
Finally, can somebody provide a sensible use case for traits, that
cannot be solved with Grafts? I am sure there is, but I am currently
lacking one.
Every time you just like to compose a class from different kinds of
behaviors Traits are superior. The use cases discussed in literature are
class hierarchies for collection classes and streams. For both
hierarchies, the interweaving of methods from different traits is a
helpful property to build all variations of semantics.
Kind Regards
Stefan
marcus
Best regards,
Marcus
Hej Marcus,
I like your approach. It is clear and simple. It would probably solve
90% of the cases, where delegation is needed.
I want to add that a manually defined method should automatically
overwrite a delegated method of the same name.
But I also want to bring up two reasonable situations your approach does
not cover. Your approach does not allow to delegate only a selection of
methods. This makes sense in order to limit the delegation to known
functionality. To stick to your example, without selective delegation,
new methods added to Counter would be automatically delegated to
CounterUser. This might break things.
A second situation not covered by your approach is renaming of delegated
methods. This can be desirable due to name clashes or name changes for
better understandability.
Of course there is always the fall back to manually implementing
delegation. And your approach alone would already be quite useful
syntactic sugar. But additional syntactic sugar for the two situations
described above would still be nice :).
Another thing. I am not sure if you intended that, but I think
delegation should not force using an Interface definition. Interfaces
are helpful in combination with type hinting, but for people who prefer
duck typing, it is reasonable to not explicitly define them. Let's leave
this choice to the people :).
Best regards
Christopher
Hello Christopher,
Tuesday, November 25, 2008, 3:27:45 AM, you wrote:
Hej Marcus,
I like your approach. It is clear and simple. It would probably solve
90% of the cases, where delegation is needed.
I want to add that a manually defined method should automatically
overwrite a delegated method of the same name.
But I also want to bring up two reasonable situations your approach does
not cover. Your approach does not allow to delegate only a selection of
methods. This makes sense in order to limit the delegation to known
functionality. To stick to your example, without selective delegation,
new methods added to Counter would be automatically delegated to
CounterUser. This might break things.
A second situation not covered by your approach is renaming of delegated
methods. This can be desirable due to name clashes or name changes for
better understandability.
Of course there is always the fall back to manually implementing
delegation. And your approach alone would already be quite useful
syntactic sugar. But additional syntactic sugar for the two situations
described above would still be nice :).
Another thing. I am not sure if you intended that, but I think
delegation should not force using an Interface definition. Interfaces
are helpful in combination with type hinting, but for people who prefer
duck typing, it is reasonable to not explicitly define them. Let's leave
this choice to the people :).
Your points are all well observed. And yes I limit what my approach can
do in order to Keep It Simple Safe (KISS). While still being able to
manually interfere with/overload the autogenerated code when necessary.
At the same time forcing to delegate to a defined Interface and not
allowing to rename on the fly prevents abusing the functionality and
helps maintainability. On the other hand it forces you to think before
coding as this approach comes with a lack of functionality.
Maybe in a second step we could make the defined interface optional but
with my implementation in mind that might not be the easiest thing to do.
Best regards
Christopher
Best regards,
Marcus
Hi,
Your points are all well observed. And yes I limit what my approach
can
do in order to Keep It Simple Safe (KISS). While still being able to
manually interfere with/overload the autogenerated code when
necessary.
At the same time forcing to delegate to a defined Interface and not
allowing to rename on the fly prevents abusing the functionality and
helps maintainability. On the other hand it forces you to think before
coding as this approach comes with a lack of functionality.
Ok, but now we start to talk about two clearly different things.
On the one hand something like traits, mixins, or what ever, and
restricted syntax sugar for delegation on the other hand.
While designing grafts, the idea was to provide a similar power like
traits do, but introducing state and encapsulation. From my point of
view, there is no other way to achieve this. At least I have not found
it.
All things in between, like "unbreakable traits" add more complexity
then they solve problems.
So actually, we are somewhere near the point which I intended to reach
with my proposal.
The questions we should answer now are:
- Does PHP need horizontal reuse?
- Do we go for an explicit solution? (traits)
- Do we go for some sort of syntax sugar for delegation? (grafts,
delegate keyword) - Or do we go the implicit way? (mixins, phyton multiple-inheritance)
Nevertheless, I would love to get some more comments on grafts before
answering this questions, because this could give additional insights
in the real problems with this approach and some ideas for improvements.
Best Regards
Stefan
Interfaces are helpful in combination with type hinting, but for
people who prefer
duck typing, it is reasonable to not explicitly define them.
It sounds like you are saying that if you prefer duck typing then
interfaces have no use.
If this is what you mean, I disagree strongly.
Interfaces are primarily to create a set of methods that define an
interface. Then the compiler can enforce implementation of that
interface in any class that claims to implement the interface. This is
the core utility of interfaces.
Alan
Hi Christopher,
thanks for (re-)starting the discussion how to make horizontal reuse
easier in PHP.
Am Donnerstag, den 13.11.2008, 16:59 +0100 schrieb Christopher Vogt:
[...]
I had a chat with Stefan about his Traits/Grafts RFC and he suggested we
should rather continue a discussion here.
I really liked to see the Grafts proposal. In traits and regular
inheritance the implementation code of several entities is somehow mixed
and as a result one entities code can break another entities code.
To keep things fair, there were proposals to keep traits completely
stateless and to allow abstract methods in traits, so that traits can
sort of "request" the implementation of a certain method. This would
keep the inheritance chain pretty stable.
The
great thing about Grafts is that the implementation is not mixed. Grafts
are completely encapsulated entities with their own state. I think in
many cases where inheritance or traits seem tempting, complete
encapsulation is actually the cleaner solution.
I see the point of having encapsulated state, I still wonder if it would be better for us to have something simpler. Simpler, as in avoiding state at all. So if we had traits without state at all a) the concept is easy to explain (like "interfaces define the API, classes keep state, traits encapsulate implementation"). A nice and easy explainable hierarchy of responsibilities.
[... Details about grafts ...]
However a problem remains. The methods of counter return $this (aka
implement a fluent interface). One idea that came to my mind to solve
this problem: PHP could provide a keyword fluent that replaces a methods
return value to form a fluent interface i.e. return $this:class QueueTicketPrinter{
use Counter as private $counter{
public fluentcurrent()
;
public fluentreset()
;
}
public function takeNumber(){
print 'your number is ".$this->counter->current();
$this->counter->inc();
}
}The keyword fluent ignores whatever value the Counter function may
return and returns an the instance of QueueTicketPrinter instead.
That's a good example why I have the feeling they are a hack. I mean a
"fluent" keyword just for a single use case. Another thing, how could I
return the class name of the current class? "public class current()
".
And so on. At the end it is an issue of "late graft binding" (similar to
late static binding).
Finally, can somebody provide a sensible use case for traits, that
cannot be solved with Grafts? I am sure there is, but I am currently
lacking one.
I can't think of any (except from late graft binding), however not
having anything against does not mean it is a good idea :) I still
prefer traits as they are easier to explain from my point of view, the
concept is more clear and that's the spirit that fits pretty well for
PHP.
cu, Lars
Aloha,
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!
regards,
Lukas
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
StefanRequest 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 <php.at.stefan-marr.de> :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. :: <?php 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. :: <?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 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. :: <?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, 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. :: <?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 are not 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, 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. :: <?php 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. :: <?php 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:: <?php 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. :: <?php 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. :: <?php 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. :: <?php 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:: <?php 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:: <?php 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:: <?php 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:: <?php 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``:: <?php $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:: <?php 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:: <?php 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:: <?php 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:: <?php 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:: <?php 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:: <?php 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:: <?php 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. --
DJ Suicide Dive
dj@ifnotwhynot.me
Lukas Kahwe Smith
mls@pooteeweet.org
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,
LukasHello,
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
StefanRequest 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 <php.at.stefan-marr.de> :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. :: <?php 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. :: <?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 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. :: <?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, 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. :: <?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 are not 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, 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. :: <?php 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. :: <?php 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:: <?php 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. :: <?php 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. :: <?php 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. :: <?php 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:: <?php 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:: <?php 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:: <?php 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:: <?php 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``:: <?php $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:: <?php 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:: <?php 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:: <?php 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:: <?php 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:: <?php 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:: <?php 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:: <?php 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. --
DJ Suicide Dive
dj@ifnotwhynot.meLukas Kahwe Smith
mls@pooteeweet.org--
--
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
Hi!
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. :)
I thought before merging code it would be useful to have some discussion
on if the code is actually doing what we want. If it's based on
http://wiki.php.net/rfc/horizontalreuse then for example I can see some
potential issues for bytecode caching related to renaming and changing
visibility.
Also, it is not clear that we want grafts there at all, some aspects of
them seem to get too hairy, esp. $this issue or statics.
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
Hi!
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. :)I thought before merging code it would be useful to have some
discussion on if the code is actually doing what we want. If it's
based on http://wiki.php.net/rfc/horizontalreuse then for example I
can see some potential issues for bytecode caching related to
renaming and changing visibility.
Also, it is not clear that we want grafts there at all, some aspects
of them seem to get too hairy, esp. $this issue or statics.
i think Stefan is fully aware of that probably there will be a
discussion first ..
but i think we all agree that this feature is very high on the list of
what people want and therefore i wanted to get this discussion going,
so that after its concluded traits can be commited to HEAD.
regards,
Lukas Kahwe Smith
mls@pooteeweet.org
I'm very concerned about the practical consequences of introducing
traits without state.
Lacking support for properties (state) in traits, programmers will
immediately start hacking properties in anyway.
I see two approaches:
A. They could do this using static hashes in global variables or a
separate class. The result is a garbage collection failure as these
references to $this will never go away. Designed-in GC failures are
unacceptable post PHP 5.3.
B. PHP allows you to define properties at runtime. That is, you can write:
$this->title = 'my title';
Without declaring var $title; at the class level.
But PHP 6 seems to be moving toward more strictness, not less, and
this style encourages the use of undeclared variables with the
resulting potential for missed bugs.
Also, the 'instead' keyword is baffling - it could mean replace A with
B or replace B with A depending on how you place your mental comma - I
much prefer the assignment syntax alternative, or 'insteadof' which is
unambiguous.
Hi!
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. :)I thought before merging code it would be useful to have some discussion
on if the code is actually doing what we want. If it's based on
http://wiki.php.net/rfc/horizontalreuse then for example I can see some
potential issues for bytecode caching related to renaming and changing
visibility.
Also, it is not clear that we want grafts there at all, some aspects of
them seem to get too hairy, esp. $this issue or statics.i think Stefan is fully aware of that probably there will be a discussion
first ..
but i think we all agree that this feature is very high on the list of what
people want and therefore i wanted to get this discussion going, so that
after its concluded traits can be commited to HEAD.regards,
Lukas Kahwe Smith
mls@pooteeweet.org--
--
Tom Boutell
P'unk Avenue
215 755 1330
punkave.com
window.punkave.com
Personally, I don't get deep into how PHP works inside, I'm just a
PHP-programmer (the man from userland). For me it will surely be
better to use 'insteadof', not 'instead'. And It seems to me more
natural to use the following syntax when using grafts:
class Foo {
use MyGraft {
public graftedClassMethodBar() as bar();
}
}
$foo = new Foo();
$foo->bar();
2009/10/15 Tom Boutell tom@punkave.com:
I'm very concerned about the practical consequences of introducing
traits without state.Lacking support for properties (state) in traits, programmers will
immediately start hacking properties in anyway.I see two approaches:
A. They could do this using static hashes in global variables or a
separate class. The result is a garbage collection failure as these
references to $this will never go away. Designed-in GC failures are
unacceptable post PHP 5.3.B. PHP allows you to define properties at runtime. That is, you can write:
$this->title = 'my title';
Without declaring var $title; at the class level.
But PHP 6 seems to be moving toward more strictness, not less, and
this style encourages the use of undeclared variables with the
resulting potential for missed bugs.
Also, the 'instead' keyword is baffling - it could mean replace A with
B or replace B with A depending on how you place your mental comma - I
much prefer the assignment syntax alternative, or 'insteadof' which is
unambiguous.Hi!
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. :)I thought before merging code it would be useful to have some discussion
on if the code is actually doing what we want. If it's based on
http://wiki.php.net/rfc/horizontalreuse then for example I can see some
potential issues for bytecode caching related to renaming and changing
visibility.
Also, it is not clear that we want grafts there at all, some aspects of
them seem to get too hairy, esp. $this issue or statics.i think Stefan is fully aware of that probably there will be a discussion
first ..
but i think we all agree that this feature is very high on the list of what
people want and therefore i wanted to get this discussion going, so that
after its concluded traits can be commited to HEAD.regards,
Lukas Kahwe Smith
mls@pooteeweet.org--
--
Tom Boutell
P'unk Avenue
215 755 1330
punkave.com
window.punkave.com--
--
Regards,
Victor
Hi:
I'm very concerned about the practical consequences of introducing
traits without state.
Well, we have not found a sufficient design until now, which would fit
nicely into PHP from dynamic and simplicity perspective.
A. They could do this using static hashes in global variables or a
separate class. The result is a garbage collection failure as these
references to $this will never go away. Designed-in GC failures are
unacceptable post PHP 5.3.
Doing this would be less than advisable.
B. PHP allows you to define properties at runtime. That is, you can
write:$this->title = 'my title';
Without declaring var $title; at the class level.
But PHP 6 seems to be moving toward more strictness, not less, and
this style encourages the use of undeclared variables with the
resulting potential for missed bugs.
Thats the currently preferred approach from my perspective.
The only thing you are going to lose here is a notion of separation of
state from different traits, compared to a language supported approach.
However, if you prefer to be more strict, it is still perfectly
possible to circumvent this problem by using getters and setters.
Also, the 'instead' keyword is baffling - it could mean replace A with
B or replace B with A depending on how you place your mental comma - I
much prefer the assignment syntax alternative, or 'insteadof' which is
unambiguous.
Yes, that was discussed before, but unfortunately did not make it into
the list of alternative syntaxes, will try to keep track of it.
Best regards
Stefan
On Wed, Oct 14, 2009 at 4:07 PM, Lukas Kahwe Smith
mls@pooteeweet.org wrote:Hi!
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. :)I thought before merging code it would be useful to have some
discussion
on if the code is actually doing what we want. If it's based on
http://wiki.php.net/rfc/horizontalreuse then for example I can see
some
potential issues for bytecode caching related to renaming and
changing
visibility.
Also, it is not clear that we want grafts there at all, some
aspects of
them seem to get too hairy, esp. $this issue or statics.i think Stefan is fully aware of that probably there will be a
discussion
first ..
but i think we all agree that this feature is very high on the list
of what
people want and therefore i wanted to get this discussion going, so
that
after its concluded traits can be commited to HEAD.regards,
Lukas Kahwe Smith
mls@pooteeweet.org--
--
Tom Boutell
P'unk Avenue
215 755 1330
punkave.com
window.punkave.com--
--
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
Hi:
I'm very concerned about the practical consequences of introducing
traits without state.Well, we have not found a sufficient design until now, which would fit
nicely into PHP from dynamic and simplicity perspective.
Could you elaborate a little? I'm having trouble understanding why we
can't just implement trait properties according to exactly the same
policy we're using for methods with regard to conflict
resolution/renaming and all the rest of it.
B. PHP allows you to define properties at runtime. That is, you can write:
$this->title = 'my title';
Without declaring var $title; at the class level.
Thats the currently preferred approach from my perspective.
The only thing you are going to lose here is a notion of separation of state
from different traits, compared to a language supported approach.However, if you prefer to be more strict, it is still perfectly possible to
circumvent this problem by using getters and setters.
So the trait would continue to use this tactic (runtime assignment to
previously undeclared properties) internally, and wrap that in getters
and setters...
Will PHP ever include optional warnings for the use of undeclared
properties? If so, then it doesn't make sense to also institutionalize
their use as the only right way to have state in traits.
--
Tom Boutell
P'unk Avenue
215 755 1330
punkave.com
window.punkave.com
Hi!
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. :)I thought before merging code it would be useful to have some
discussion on if the code is actually doing what we want. If it's
based on http://wiki.php.net/rfc/horizontalreuse then for example I
can see some potential issues for bytecode caching related to
renaming
and changing visibility.
Also, it is not clear that we want grafts there at all, some aspects
of them seem to get too hairy, esp. $this issue or statics.i think Stefan is fully aware of that probably there will be a
discussion first ..
but i think we all agree that this feature is very high on the list of
what people want and therefore i wanted to get this discussion going,
so that after its concluded traits can be commited to HEAD.
Has it been discussed to add a portion of the patch to HEAD?
Reading up on scala, I really like what they've done with traits (as a
developer):
http://www.scala-lang.org/node/117
I've updated the RFC with an example:
http://wiki.php.net/rfc/traits#scala_synthax_and_practical_example
Hopefully, people can agree on a simple patch that ignores conflict
resolution / aliasing solution and just adds:
trait User_Comments {}
class App_Document with User_Comments {}
// Or the original syntax
class App_Email {
use User_Comments;
}
The details of conflict resolution, if the use cases are strong enough can
come later.
Grafts well... the re-use is interesting but I worry about how php is going
to evolve is you start offering that much flexibility.
Just my thoughts, great work though.
Hi:
Has it been discussed to add a portion of the patch to HEAD?
Hopefully, people can agree on a simple patch that ignores conflict
resolution / aliasing solution and just adds:
Ehm, hopefully not...?
The power of traits is the explicit resolution of conflicts.
And at least for me, that is the interesting part here.
I agree that mixins (traits without explicit conflict resolution) are
easier to begin with, but they have problem in the long run.
Linearization introduces brittle architectures, which does not cope
well with changes.
Tom mentioned that he thinks that PHP6 is moving in a more strict
direction, probably that is also motivated by the large systems
already out there, for which people tend to prefer more strict
language features.
In this sense, an explicit conflict resolution is a must-have.
Anyway, could I ask you to move your changes from the outdated RFC
over to the one we are talking about? (http://wiki.php.net/rfc/horizontalreuse
)
Best
Stefan
PS: Is there a way to have a watermark or something which marks the
old RFC as outdated? I don't want to delete it for documentation
purpose...
trait User_Comments {}
class App_Document with User_Comments {}
// Or the original syntax
class App_Email {
use User_Comments;
}The details of conflict resolution, if the use cases are strong
enough can
come later.Grafts well... the re-use is interesting but I worry about how php
is going
to evolve is you start offering that much flexibility.Just my thoughts, great work though.
--
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
I thought before merging code it would be useful to have some
discussion on if the code is actually doing what we want. If it's
based on http://wiki.php.net/rfc/horizontalreuse then for example I
can see some potential issues for bytecode caching related to
renaming and changing visibility.
Sure, the discussion will be necessary to fix the final design of the
remaining rough edges like choice of keywords.
The current state of the code is basically documented by the test case.
They cover all syntactical features and their implications for using
traits.
However, this functionality is covered by the Traits part of the RFC
as well.
Also, it is not clear that we want grafts there at all, some aspects
of them seem to get too hairy, esp. $this issue or statics.
Personally, I see question of Traits or Grafts as an exclusive one. It
would not do any good to have both of them. And, grafts have not been
implemented.
Best regards
Stefan
--
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com--
--
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