Hi Stefan,

Appreciate you taking the time to discuss this - and I apologize if I
jumped the gun with some of these comments.

I knew about the traits features in Scala, and I guess I assumed this would
be similar - not so.

Reading through my own remarks and your comments, I now have a better
understanding of what traits are, and how they're intended to be used.

I believe the key to understanding traits, is understanding that traits are
in fact an implementation detail - an artifact that does not really change
or affect the nature of OOP in PHP as such, and by design, should not. I
understand that now - thank you :-)

>From my perspective, a key difference from classes and interfaces, is that
traits have no meaningful use at run-time - no type-hinting and with no
real reflection-features that reveal the details. They cannot implement
interfaces for classes, but classes can use them to implement interfaces.
Traits just provide a set of method implementations that can be aggregated
by classes. So in a sense, they're a design-time tool for the programmer.

So I guess my remaining quibble is that the documentation needs to relay
this somehow - with a basic real-world example that actually uses an
interface too, to clarify the difference, and to demonstrate how this tool
use useful in conjunction with class and interface declarations. Hello
world really doesn't explain anything, other than the syntax.

Here's a better example of something useful that actually works:

  trait Accessors
  {
    public function __get($name)
    {
      return $this->{'get'.$name}();
    }

    public function __set($name, $value)
    {
      $this->{'set'.$name}($value);
    }
  }

  class OrderLine
  {
    use Accessors;

    public $price;
    public $amount;

    public function getTotal()
    {
      return $this->price * $this->amount;
    }
  }

  $line = new OrderLine;

  $line->price = 20;
  $line->amount = 3;

  echo "Total cost: ".$line->total;

This may be a little too magical for an example in the documentation though
- and doesn't demonstrate interfaces.

I'll ponder this and post a better example if I can think of one... :-)

And just one other comment:

> > var_dump($test instanceof CartBehavior); // => false

> Why would it be beneficial to throw an exception here?

CartBehavior is a trait, and not a class or interface. The instanceof
operator has no meaning for traits - it always returns false.

You could argue that false is the expected result - I would argue that the
only reason this happens to work at all, is because traits internally are
classes.

Suppose you thought you were actually doing a meaningful type-check of some
sort? If by mistake you put a trait-name where you meant to put the name of
an interface or class, such an error could be very hard to spot.

Since there is no such thing as an "instance of" a trait, the instanceof
operator should not work for traits.

Once again, thank you for taking the time to talk about this, Stefan - I
hope this feedback is useful :-)

- Rasmus

On Fri, Nov 11, 2011 at 12:02 PM, Stefan Marr <p...@stefan-marr.de> wrote:

> Hi Rasmus:
>
> First, sorry, I don't have currently the time to reiterate all discussions
> on these questions.
> Please, do me the favor and search the archives for previous discussions.
> I believe _all_ points you raise here have been discussed and commented
> before, and most of them quite recently.
>
> Since I believe the answers are already given,
> let me try another approach to tackle your problems.
> From my perspective the purpose of traits is often misunderstood.
> So, let me ask a few questions. They might sound basic,
> but I want to better understand your design rational,
> and the reasons behind your implementation approach.
>
> If you got the time, please try to answer all of them.
> They might seem suggestive, or even worse, 'rhetorical',
> but that is not on purpose.
>
> Answering them will help us all better to understand
> what the underlying issues/conceptual problems are we need to tackle,
> either by improving the current implementation and/or documentation.
>
> On 11 Nov 2011, at 17:00, Rasmus Schultz wrote:
>
> > class Cart
> > {
> >  public static $instance;
> >
> > # public function addItem(CartBehavior $item, $amount=1) // => script
> > terminates
> >  public function addItem($item, $amount=1)
> >  {
> >    echo "Adding {$amount} {$item->getName()} to shopping cart - price =
> > ".($amount * $item->getPrice())."\n\n";
> >  }
> > }
>
> What is the real purpose of adding the type hint for CartBehavior here?
> My understanding would be, you want to express/document that the given
> object is required to support a specific set of operations.
>
> Is there any other semantics that need to be expressed by this annotation?
>
> On a practical level, you probably want to make sure that the engine never
> barks at you because it got an object that does not understand a certain
> operation.
>
> Is it relevant for the method using that object that it provides a
> specific implementation for the required set of operations?
> [Side note for the archive: traits do not guarantee that]
>
>
> > var_dump($test instanceof CartBehavior); // => false
> Beside testing the corner cases of the language definition,
> is there any practical need for this test?
> Do you have an example where it is desirable to know exactly
> that a particular implementation is provided by an object?
>
>
>
> > var_dump((new ReflectionClass('Product'))->getTraits()); // CartBehavior
> is
> > a ReflectionClass?
>
> Well, the reflection API is not perfect yet. Other interesting features,
> like knowing how conflicts where resolved are missing, too. (I think)
> The reason for that is unfortunately missing time.
>
>
> > Secondly, the instanceof operator doesn't complain when you test to see
> if
> > an object carries a trait - if this isn't going to work, it should at
> least
> > throw an exception.
>
> You refer to the previous example, I suppose:
> > var_dump($test instanceof CartBehavior); // => false
>
> Why would it be beneficial to throw an exception here?
> $test is not an instance of CartBehavior, since traits cannot be
> instantiated, and do not provide any guarantees in what ever respect.
> So, even if a class uses CartBehavior, you would never be sure that
> it actually also uses all of the implementations provided by it.
>
>
>
> > Lastly, when you reflect on a trait, it comes back as an instance of
> > ReflectionClass. As pointed out, traits are probably more similar to
> > interfaces than they are to classes, and they definitely don't have
> > properties, as are exposed by the ReflectionClass type.
> [Well, it can be useful to test for the properties expected by a trait.]
> Yes, that is an implementation artifact of how the reflection API is
> designed.
>
>
> > I would also point out that the examples in the documentation (which it
> > seems were just copied from the RFC?) do not demonstrate any real purpose
> > of this feature. Is this the most anybody has attempted to do with this
> > feature? Trying to come up with a real example, I didn't get very far
> > before running into these stumbling blocks.
>
> I frequently search for usages of traits on the web, and by now, there are
> quite a number
> of articles giving examples and discussing the pros and cons of traits.
> So, no, you are not the first one.
>
> But, yes, the current documentation page could use some better examples.
>
>
> > If I can't use instanceof to check for a trait (A), then I would at least
> > expect to be able to write a trait that implements an interface (B) -
> does
> > that not seem reasonable or logical?
>
> The discussion we had on that topic evolved to the following proposal:
>
> https://wiki.php.net/rfc/horizontalreuse#requiring_composing_class_to_implement_interface
>
> However, to my understanding, there was no consent on actually adding this.
>
>
> > Having to use both the interface and the trait as a pair, and having to
> > explicitly apply them both to the class, feels like a work-around.
>
> The discussions we had here suggest otherwise: this is exactly the way to
> go from a conceptual perspective.
> It keeps the two concepts apart, avoids confusion, and thus, is the
> conceptual purer alternative.
> However, by answering my questions from above, you can certainly make a
> point that the practical overhead does not justify this conceptual burden.
>
> > I apologize if I'm somehow missing the big picture here, or maybe I set
> my
> > expectations too high - but my first impression of this feature is that
> > it's crippled and somewhat half-baked... If there was a deliberate and
> > logical reason for not supporting these features, I would like to
> > understand why. If not - great work so far, but perhaps this feature is
> not
> > quite mature enough for release just yet?
>
> On a personal note: never tell a mother that her child is ugly.
> Please stay constructive. Thanks!
>
> Best regards
> Stefan
>
>
> --
> Stefan Marr
> Software Languages Lab
> Vrije Universiteit Brussel
> Pleinlaan 2 / B-1050 Brussels / Belgium
> http://soft.vub.ac.be/~smarr
> Phone: +32 2 629 2974
> Fax:   +32 2 629 3525
>
>

Reply via email to