Hi,
Here is a slightly clearer syntax for trait instantiation and for
aliasing/removing method names from traits:
<?php
trait foo
{
function bar(){}
function bing(){}
}
trait bar
{
function bar(){}
}
class A
{
trait foo;
}
class B
{
trait foo {unset bing, bar as bing};
trait bar;
}
?>
Simply re-using "trait" instead of "use" is a more self-documenting
solution. I found it slightly confusing to see "use" when that is a
namespace-specific token currently. This is also in keeping with the
way functions are defined in interfaces vs. implementation, but in
reverse (for traits, brackets are used for declaration, semi-colon used
for instantiation).
Obviously, using "unset" is more similar to current PHP ideas on how to
remove something, and the "as" syntax is also very PHP-ish, these will
be far more self-documenting than the proposed syntaxes I've read thus far.
Greg
[snip...]
class A
{
trait foo;
}class B
{
trait foo {unset bing, bar as bing};
trait bar;
}
?>
[snip...]
As long as we're discussing syntax - I rather like
class A
{
attach foo;
}
class B
{
attach foo {hide bing, bar as bing};
attach bar;
}
but that's just my opinion into the "how to do the syntax" discussion
I do like the "foo as bar" style aliasing, it will really make sense as
namespaces follow the same convention, but I think hide is more
appropriate than unset
As for attach keyword, well it's short and descriptive, you are
attaching new functionality to the class. I think that reusing trait
both as the way to put the trait in the class and to declare the trait
will confuse people.
My $0.02
Thanks,
Elizabeth Smith
Simply re-using "trait" instead of "use" is a more self-documenting
solution. I found it slightly confusing to see "use" when that is a
namespace-specific token currently. This is also in keeping with the
way functions are defined in interfaces vs. implementation, but in
reverse (for traits, brackets are used for declaration, semi-colon
used
for instantiation).
I like "trait" instead of "use" or any of the other proposals as well
Obviously, using "unset" is more similar to current PHP ideas on how
to
remove something, and the "as" syntax is also very PHP-ish, these will
be far more self-documenting than the proposed syntaxes I've read
thus far.
Unset seems to send the wrong message again. "hide" might be better?
As I said before "as" for aliasing is a good choice, or "alias" to
make it even clearer.
@Stefan: are you keeping track of all the different syntax proposals?
regards,
Lukas
Hi,
Am Donnerstag, den 21.02.2008, 13:55 +0100 schrieb Lukas Kahwe Smith:
[...]
I like "trait" instead of "use" or any of the other proposals as well
Do you like "class Foo class Bar" instead of "class Foo extends Bar"?
Normally PHP uses a descriptive naming scheme, while "class Foo { trait
XY; }}" would be declarative.
cu, Lars
Hello,
What about has?
interface context {
/* ... */
}
trait contextA implements context {
/* ... */
}
class A {
has contextA;
has foobar;
}
In my opinion it is similar and descriptive like you having to implement an
interface with implements, extend a class with 'extends' and now you have a
trait with 'has'.
Do you like "class Foo class Bar" instead of "class Foo extends Bar"?
Normally PHP uses a descriptive naming scheme, while "class Foo { trait
XY; }}" would be declarative.
@Stefan: are you keeping track of all the different syntax proposals?
Yes, I try to keep step with all those different proposals and sum them
up in the end of the day.
Kind Regards
Stefan
Gregory Beaver wrote:
Here is a slightly clearer syntax for trait instantiation and for
aliasing/removing method names from traits:<?php
trait foo
...
class B
{
trait foo {unset bing, bar as bing};
trait bar;
}
I like the approach to reuse identifiers (trait both in declaration and
use, clearly links the two) and unset/as (avoids new keywords).
Some more thoughts:
- I've seen Stefan Marr's remarks about aliasing != renaming but the
only explanation I could find in the thread was about user perception
(along the line of "... would expect all uses of the function to be
renamed ..."). I don't think aliasing really helps here. I can see two
use cases for aliasing: 1) An interface uses a different name than the
trait implements. Not sure that's a software design problem trait should
try to solve. 2) There is a clash with an existing function in the
class. Then one would have to both alias and exclude the trait function
name anyway (which is the same as renaming) PLUS you'd have the problem
of trait classes calling the 'wrong' functions again. Maybe someone can
shed light on why aliasing is preferable to renaming after all. - I think the whole 'how to avoid clashes' discussion is missing one
important point: Being able to override functions defined in a trait is
a feature, not a bug. It's the same as with inheritance, i.e. if I
override functions I can enhance but also break existing code. IMHO
Traits should be a simple mechanism giving the developer the freedom to
do the wrong thing ;-) Or more seriously: If you want to have
encapsulated functionality used in multiple places I'd use delegates a
la foo_traitemu::bar(). - The exclusion model is missing a way to include only specific
functions. To me this seems as useful as being able to exclude functions.
So my favourite solution (apart from allowing include in class
definitions ;-)) would be
trait foo { ... }
...
class B
{
trait foo; # All functions from foo
trait bar(a); # Only function a from bar
trait qux(not b); # Everythign but function b
trait quux(c as d); # Include c but rename it to d
trait quuux(not b, c as d); # Combination of the above
}
Note: The inclusion of specific functions acts as if a "not *" (while *
doesn't really need to be implemented neither for inclusion nor
exclusion IMHO) was given first.
Another detail: The implementation of the parser changes should still
allow a class or function called "trait", i.e. "trait" should only be a
keyword at specific positions in the source to avoid unneccesary BC
breaks. The current patch has this BC problem.
- Chris
Christian Schneider wrote:
So my favourite solution (apart from allowing include in class
definitions ;-)) would be
trait foo { ... }
...
class B
{
trait foo; # All functions from foo
trait bar(a); # Only function a from bar
trait qux(not b); # Everythign but function b
trait quux(c as d); # Include c but rename it to d
trait quuux(not b, c as d); # Combination of the above
}
I could live with most of this as well.
"trait bar(a);" is really confusing - it looks like a function call and
is not nearly as clear as the other options. It's unlikely to be
necessary since a trait should really only have a few functions in it
anyways.
Note: The inclusion of specific functions acts as if a "not *" (while *
doesn't really need to be implemented neither for inclusion nor
exclusion IMHO) was given first.Another detail: The implementation of the parser changes should still
allow a class or function called "trait", i.e. "trait" should only be a
keyword at specific positions in the source to avoid unneccesary BC
breaks. The current patch has this BC problem.
This is not possible to implement, having tried to do a similar thing
for 'import' and 'namespace.' The reason is that we can encounter a
classname at any point thanks to "classname::whatever" syntax, so it
slows the lexer down a bit in that for every T_TRAIT
we would have to
check to see if the next 2 characters are ::, and makes the lexer
uber-complicated. It's a big mess.
Greg
Another detail: The implementation of the parser changes should still
allow a class or function called "trait", i.e. "trait" should only
be a
keyword at specific positions in the source to avoid unneccesary BC
breaks. The current patch has this BC problem.This is not possible to implement, having tried to do a similar thing
for 'import' and 'namespace.' The reason is that we can encounter a
classname at any point thanks to "classname::whatever" syntax, so it
slows the lexer down a bit in that for everyT_TRAIT
we would have to
check to see if the next 2 characters are ::, and makes the lexer
uber-complicated. It's a big mess.
Not yet at any rate. From what I hear the next step after migration to
re2c [1] is to migrate to lemon, which should make stuff like this
possible. However while re2c has a chance of making it into PHP 5.3,
the lemon part seems a bit further off.
regards,
Lukas
Gregory Beaver schrieb:
Christian Schneider wrote:
Another detail: The implementation of the parser changes should still
allow a class or function called "trait", i.e. "trait" should only be a
keyword at specific positions in the source to avoid unneccesary BC
breaks. The current patch has this BC problem.This is not possible to implement, having tried to do a similar thing
for 'import' and 'namespace.' The reason is that we can encounter a
classname at any point thanks to "classname::whatever" syntax, so it
slows the lexer down a bit in that for everyT_TRAIT
we would have to
check to see if the next 2 characters are ::, and makes the lexer
uber-complicated. It's a big mess.
I think this is a point we should take seriously. I see the following
solutions:
- Wait with the introduction until either the parser allows such an
addition without BC break or wait for the appropriate PHP release. (Is
5.3 the time to introduce this BC break? I don't know the current PHP
definitions about major/minor releases) - Consider this issue carefully when deciding on a syntax. Maybe favour
a syntax with fewer new keywords. Reusing keywords like "include", "as"
or "not" come to mind.
- Chris
Another detail: The implementation of the parser changes should still
allow a class or function called "trait", i.e. "trait" should only be a
keyword at specific positions in the source to avoid unneccesary BC
breaks. The current patch has this BC problem.This is not possible to implement, having tried to do a similar thing
for 'import' and 'namespace.' The reason is that we can encounter a
classname at any point thanks to "classname::whatever" syntax, so it
slows the lexer down a bit in that for everyT_TRAIT
we would have to
check to see if the next 2 characters are ::, and makes the lexer
uber-complicated. It's a big mess.I think this is a point we should take seriously. I see the following
solutions:
- Wait with the introduction until either the parser allows such an
addition without BC break or wait for the appropriate PHP release. (Is
5.3 the time to introduce this BC break? I don't know the current PHP
definitions about major/minor releases)- Consider this issue carefully when deciding on a syntax. Maybe favour
a syntax with fewer new keywords. Reusing keywords like "include", "as"
or "not" come to mind.
If the namespace patch is in PHP 5.3 (it is in the snapshots) then we
are already introducing the new keyword namespace
that will break BC.
I think we can take this as reason enough to go ahead and add the
trait
keyword. And since we have the following complementary keywords:
-
class
andextends
-
interface
andimplements
-
namespace
anduse
- etc.
we should go ahead and introduce the complimentary keyword for trait
,
and it doesn't make much sense to reuse one of the other keywords. There
are already some suggestions on Stefan's RFC, but none of them seem to
fit as well to me. I think we should throw import
back onto namespaces
and use use
with traits. The use
or uses
keyword is consistent
with other implementations of traits. And if nobody likes that idea,
what about inline
, inject
, insert
, add
, or even +
.
I also have an idea for aliasing that I don't think anyone has thought
of yet: reusing the clone
keyword like so:
<?php
class Foo {
use Bar {
not foo1;
clone foo1 as barFoo1;
}
}
Cloning I think gives us the correct idea of what is actually happening:
the entire body of method foo1 is copied to a new method called barFoo1.
Also, using not
for exclusion also gets my vote (although I doubt my
vote actually matters).
--
"Joshua Thompson" spam.goes.in.here@gmail.com
<http://www.schmalls.com
Hi,
Christian Schneider schrieb:
I can see two
use cases for aliasing:
- An interface uses a different name than the
trait implements. Not sure that's a software design problem trait should
try to solve.
Well, no, aliasing shouldn't be used to achieve this, think it would be
better to add some glue code to a class to fulfill a interface, i.e. add
a new method which will call the method from the trait if the name of
the traits method does not fit to the interface. This will avoid
confusion about the next point you've named. (for simple situations
exclude and aliasing will do the job as well, yes)
- There is a clash with an existing function in the
class. Then one would have to both alias and exclude the trait function
name anyway (which is the same as renaming) PLUS you'd have the problem
of trait classes calling the 'wrong' functions again.
In my opinion it is not a problem, but an opportunity.
For a situation where two traits are implementing stuff which is very
closely related and will be combined in a class you can do the following:
trait A {
public function handle($something) {
$this->process($something);
}
protected function process($something) {
// ...
}
}
trait B {
protected function process($something) {
// a method to process $something and be reused
// interdependently at several places
}
}
class Processor {
use A {
processA => process
}
use B {
processB => process
}
protected function process($something) {
// do combined processing using both traits methods
$this->processA($something);
$this->processB($something);
}
}
Kind Regards
Stefan
Hi,
what's about sample below?
<?php
class foo
{
public $aabb;
const bbaa = 'abba';
static public $baba;
function bar(){}
function bing(){}
}
class bar
{
const baab = foo::bbaa;
function bar(){}
}
class A
{
trait foo;
}
class B
{
trait foo {unset bing, bar as bing, unset $aabb, unset bbaa};
trait bar;
}
?>
in other words, why to introduce such a new thing as trait instead of using
classes and trait'ing them?
just my two cents.
-j
"Gregory Beaver" greg@chiaraquartet.net wrote in message
news:47BD207C.2080905@chiaraquartet.net...
Hi,
Here is a slightly clearer syntax for trait instantiation and for
aliasing/removing method names from traits:<?php
trait foo
{
function bar(){}
function bing(){}
}trait bar
{
function bar(){}
}class A
{
trait foo;
}class B
{
trait foo {unset bing, bar as bing};
trait bar;
}
?>Simply re-using "trait" instead of "use" is a more self-documenting
solution. I found it slightly confusing to see "use" when that is a
namespace-specific token currently. This is also in keeping with the
way functions are defined in interfaces vs. implementation, but in
reverse (for traits, brackets are used for declaration, semi-colon used
for instantiation).Obviously, using "unset" is more similar to current PHP ideas on how to
remove something, and the "as" syntax is also very PHP-ish, these will
be far more self-documenting than the proposed syntaxes I've read thus
far.Greg
Hi,
jvlad schrieb:
in other words, why to introduce such a new thing as trait instead of using
classes and trait'ing them?
I've introduced it as a separate notion from classes to avoid
misconception and problems occurring from conflicting properties and
constant definitions.
Your example demonstrates the week point of this approach very well in
my eyes. Instead of allow easy reuse, you will have to manage much more
conflicts. Think it is better to restrict the idea. Just allow to reuse
methods trying to avoid additional conflicts which must be handled each
time.
Another point is, think there is no benefit in duplicating constants?.
Kind Regards
Stefan
"Stefan Marr" php@stefan-marr.de wrote in message
news:47BE014F.5020202@stefan-marr.de...
Hi,
jvlad schrieb:
in other words, why to introduce such a new thing as trait instead of
using classes and trait'ing them?
I've introduced it as a separate notion from classes to avoid
misconception and problems occurring from conflicting properties and
constant definitions.Your example demonstrates the week point of this approach very well in my
eyes. Instead of allow easy reuse, you will have to manage much more
conflicts. Think it is better to restrict the idea. Just allow to reuse
methods trying to avoid additional conflicts which must be handled each
time.Another point is, think there is no benefit in duplicating constants?.
Why would this create any problems? Say, you have class B that extends class
A and both do define one method and one property under the same names. Will
this create a problem? No. It's because there are rules that clearly
describe how it works (method and property will be overriden). Similar
approach may be applied to trait'ing classes. Why not? For example if you
have a property defined above trait'ing, it will be overriden by the trait
if it comes up with its own property with the same name (same goes for the
properties defined in the ancestor class). If trait defines a property, it
can be overriden by class if it introduce it below trait'ing statement.
On the other hand, if someone being a developer cares of potential
"conflict" (unwanted override), he/she may simply not define properties and
constants in the class that's intended to be trait'ed into another class.
Best Regards
j
Hi,
jvlad schrieb:
Why would this create any problems? Say, you have class B that extends class
A and both do define one method and one property under the same names. Will
this create a problem? No. It's because there are rules that clearly
describe how it works (method and property will be overriden). Similar
approach may be applied to trait'ing classes. Why not? For example if you
have a property defined above trait'ing, it will be overriden by the trait
if it comes up with its own property with the same name (same goes for the
properties defined in the ancestor class). If trait defines a property, it
can be overriden by class if it introduce it below trait'ing statement.
On the other hand, if someone being a developer cares of potential
"conflict" (unwanted override), he/she may simply not define properties and
constants in the class that's intended to be trait'ed into another class.
This contradicts the idea behind traits.
Here all conflicts are handled explicitly. Overriding is not used
between traits to provide greater power and allow the notion of
flattening in the way it has been proposed.
Your proposal is much like mixins. But mixins have limited composition
power caused by this overriding. There are situations without a suitable
linearization to get overlapping methods from to different mixins in the
needed combination.
The complete argumentation can be found here:
http://www.iam.unibe.ch/~scg/Archive/Papers/Duca06bTOPLASTraits.pdf
illustrating the cons of mixins and multiple inheritance.
Kind Regards
Stefan
jvlad schrieb:
in other words, why to introduce such a new thing as trait instead of using
classes and trait'ing them?I've introduced it as a separate notion from classes to avoid misconception
and problems occurring from conflicting properties and constant definitions.
And that is something we need, as I had a hard time parsing the previous
example without this.
Derick
--
Derick Rethans
http://derickrethans.nl | http://ezcomponents.org | http://xdebug.org