On 07/09/2016 15:09, Michał Brzuchalski wrote:
@Mathieu How about that: https://gist.github.com/brzuchal/e7b721e22a19cca42ec0d1f597a23baf We've discussed this could be best option, when invoking `transformer` method (or whatever we call it)
there is `$this = clone $this` invoked under the hood.
I don't know, it seems a bit strange to override $this . I can't have the original object and the new one at the same time.
Or do you mean I also have the option to do "$clone = clone $this" ?

2016-09-07 14:53 GMT+02:00 Mathieu Rochette <math...@rochette.cc <mailto:math...@rochette.cc>>:

    a few remarks on mutator methods:

      * It could be a nice way to solve the "create another one almost
        the same" use case.
      * I don't mind if $clone is an explicit parameter or magically
        available
      * what happens if I call other function/methods with this $clone
        before the end of the function ?
      * and the only downside: I have to make a method just for
        cloning. that means I have to call a mutator multiple times if
        I want to make a bunch of clone, eg:

    because of the last point, I think I'd like the seal the clone at
    then end of the block/method better, here are 2 examples to
    illustrate what I mean

    class immutable foo {
      private $prop = 0;

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

      public function bar(obj $o) {
        $e->makeSomethingWith($this->cloneAndEdit(42));
      }

      public function many($n) {
        $a = [];
        for ($i = 0; $i < $n; $i++) {
          $a[] = $this->cloneAndEdit($i));
        }
        return $a;

      }

      public function mut cloneAndEdit($n) {
        $clone->prop = $n;
      }
    }

    // vs

    class immutable foo {
      private $prop = 0;

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

      public function bar(obj $o) {
        $c = $clone $this;
        $c->prop = 42;
        $e->makeSomethingWith($c);
      }

      public function many($n) {
        $a = [];
        for ($i = 0; $i < $n; $i++) {
    $a[] = $c = $clone $this;
          $c->prop = 42;
        }
        return $a;
      }
    }

    I understand that it's still not clear exactly when the object
    should be sealed but if it can works this one would have my preference

    On 04/09/2016 14:10, Michał Brzuchalski wrote:
    2016-09-04 10:55 GMT+02:00 Fleshgrinder<p...@fleshgrinder.com> 
<mailto:p...@fleshgrinder.com>:

    Hi Chris!

    On 9/3/2016 5:00 PM, Chris Riley wrote:
    - Properties can be declared immutable. Immutable properties may only be
    changed under two circumstances: a) In the objects constructor b) If they
    are null (This enables setter injection if required)

    The constraint b) would make the object mutable and defeat the purpose
    of the immutable modifier since any property could change at any time if
    it was NULL at the beginning. Requiring syncing in concurrent environments.

    On 9/3/2016 5:00 PM, Chris Riley wrote:
    - Arrays assigned to immutable properties would not be possible to change

    Array support would definitely be very nice. I mean, we have constant
    arrays already, hence, it is possible.

    On 9/3/2016 5:00 PM, Chris Riley wrote:
    - Objects assigned to immutable properties would be possible to change,
    so
    long as the same object remained assigned to the property.

    This would once more lead to mutability and the constraint of
    immutability would be violated.

    On 9/3/2016 5:00 PM, Chris Riley wrote:
     From a developer adoption point of view, I think these two points are
    important to making immutable classes generally useful. Without 1, it
    will
    be a nuisance to use 3rd party libraries esp those which retain
    compatibility for PHP < 7.2. Without 2 you block the ability to use
    setter
    injection, which I personally would be in favour of if it meant that devs
    stopped using it - it wouldn't - they would simply not use immutable
    classes, loosing the benefits thereof.

    The adoption of the feature will be halted until 7.2 is widely available
    in bigger projects. That is most certainly right. However, we should aim
    for the best, most useful, and future proof solution and not towards the
    one that's adopted very fast but lacks some important constraints.
    Having truly immutable objects is required in concurrent scenarios and
    such scenarios are in the future for PHP and not in the past.

    Regarding setter injection: I do not see the need for it at all in the
    context of immutable objects. In the end we are talking about value
    objects here and they should not have any optional dependencies. Maybe
    you could come up with a use case to illustrate the need?

    On 9/3/2016 5:00 PM, Chris Riley wrote:
    Dealing with the clone issue some of my ideas since then were:

    - Seal/Unseal (As per Larry's suggestion)
    - Parameters to __clone; in this instance the clone method would be
    allowed
    to change properties of the object as well as the constructor. This feels
    like it may breach the principal of least surprise as cloning an object
    no
    longer guarantees an exact copy.
    - A new magic method __mutate($property, $newvalue) called instead of a
    fatal error when a property is changed. This probably lays too many traps
    for developers for it to be a good idea.
    - Implicitly returning a new object whenever a property is changed.
    Similar
    reservations to the above.
    - A new magic method __with($newInstance, $args) and a keyword with that
    is
    used in place of clone eg $x = $y with ($arg1, $arg2); in this instance,
    __with receives a clone of $y (after calling __clone) and an array
    [$arg1,
    $arg2] the with magic method is allowed to mutate $newInstance and must
    return it. This is currently my favoured solution

    How does one know which property is to be mutated in the __with method?
    You should also not underestimate the performance hit and the branching
    since you only want to change the properties that changed based on the
    data from the passed array.

    I have a third proposal after giving this some more thought. Inspired by
    Rust's approach to mark mutation explicitly.

       final immutable class ValueObject {

         public $value;

         public mutator [function] withValue($clone, $value): static {
           $clone->value = $value;
         }

       }


    Providing `mutator` | `mut` keyword as method modifier sounds liek a good
    idea,
    althought passing `$clone` parameter as first additional param could break
    method declaration and would be misleading.

    Assuming mutator method is designed to  return mutated clone of immutable
    object
    having `$clone` variable could be handled internally without breaking
    method declaration.

    Such variable could be unlocked while in mutator method and locked on
    return.
    I was thinking about additional check if such mutator returns `$clone` but
    not `$this`
    but I don't see the need of it - assuming there is no what to change in some
    circumstances ther would be also possible to return `$this`.

    The return type declaration `self` could increase readability, but should
    not be required,
    as some developers doesn't already use return types.


    A mutator function always receives the mutable clone as first argument
    and always returns that one. Users can have a return but they must
    return the clone (hence the static return type declaration).

       $vo1 = new ValueObject(1);
       $vo2 = $vo1->withValue(2);

    Calls are of course without the clone as it is handled by the engine.
    There is no special branching necessary and no performance hit at all
    while the logic is kept in the place where it is required.

    --
    Richard "Fleshgrinder" Fussenegger






--
regards / pozdrawiam,
--
Michał Brzuchalski
brzuchalski.com <https://brzuchalski.com/>

Reply via email to