I understand the concerns of all of you very well and it's nice to see a
discussion around this topic. Fun fact, we are not the only ones with
these issues: https://github.com/dotnet/roslyn/issues/159

On 9/6/2016 6:01 PM, Larry Garfield wrote:
> How big of a need is it to allow returning $this instead of $clone,
> and/or can that be internalized somehow as well?  With copy-on-write,
> is that really an issue beyond a micro-optimization?

I asked the same question before because I am also unable to answer this
question regarding the engine.

However, for me it is more than micro-optimization, it is about identity.

  final class Immutable {
    // ... the usual ...
    public function withValue($value) {
      $clone = clone $this;
      $clone->value = $value;
      return $clone;
    }
  }

  $red = new Immutable('red');
  $still_red = $red->withValue('red');

  var_dump($red === $still_red); // bool(false)

This is a problem in terms of value objects and PHP still does not allow
us operator overloading. A circumstance that I definitely want to
address in the near future.

But the keyword copy-on-write leads me to yet another proposal, actually
your input led me to two new proposals.

# Copy-on-Write (CoW)
Why are we even bothering on finding ways on making it hard for
developers while the solution to our problem is directly in front of us:
PHP Strings!

Every place in a PHP program refers to the same string if that string is
the same string. In the second someone mutates that string in any way
she gets her own mutated reference to that string.

That's exactly how we could deal with immutable objects. Developers do
not need to take care of anything, they just write totally normal
objects and the engine takes care of everything.

This approach also has the advantage that the return value of any method
is (as always) up to the developers.

(Cloning is disabled and results in an error as is because it makes no
sense at all.)

# Identity
This directly leads to the second part of my thoughts and I already
touched that topic: identity. If we have two strings their binary
representation is always the same:

  var_dump('string' === 'string'); // bool(true)

This is the exact behavior one wants for value objects too. Hence,
immutable objects should have this behavior since they identify
themselves by their values and not through instances. If I create two
instances of Money with the amount 10 and the Currency EUR then they are
always the same, no matter what. This would also mean that no developer
ever needs to check if the new value is the same as the existing one,
nor does anyone ever has to implement the flyweight pattern for
immutable objects.

A last very important attribute is that it does not matter in which
thread an immutable value object is created because it always has the
same identity regardless of it.

This could easily be achieved by overwriting the object hashes
(spl_object_hash) with something that hashes based on the values, and
predictably across threads (UUIDs?).

# Full Example
<?php

final immutable class ValueObject {

  public $value;

  public function __construct($value) {
    $this->value = $value;
  }

  public function withValue($value) {
    $this->value = $value;
  }

}

class A {

  public $vo;

  public function __construct(ValueObject $vo) {
    $this->vo = $vo;
  }

}

class B {

  public $vo;

  public function __construct(ValueObject $vo) {
    $this->vo = $vo;
  }

}

$vo = new ValueObject(1);

$a = new A($vo);
$b = new B($vo);

var_dump($a->vo === $b->vo); // bool(true)

$a->vo->withValue(2);

var_dump($a->vo === $b->vo); // bool(false)

$a->vo->withValue(1);

var_dump($a->vo === $b->vo); // bool(true)

// :)

?>

-- 
Richard "Fleshgrinder" Fussenegger

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to