@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.

2016-09-07 14:53 GMT+02:00 Mathieu Rochette <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> 
> <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

Reply via email to