On 30/12/2020 13:49, Olle Härstedt wrote:
Uniqueness is when you only allow _one_ reference to an object (or
bucket of memory).
[...]
You can compare a builder pattern with immutability vs non-aliasing
(uniqueness):
```
// Immutable
$b = new Builder();
$b = $b->withFoo()->withBar()->withBaz();
myfun($b); // $b is immutable, so $b cannot be modified by myfun()
return $b;
```
```
// Uniqueness
$b = new Builder(); // class Builder is annotated as non-aliasing/unique
$b->addFoo();
$b->addBar();
$b->addBaz();
myfun(clone $b); // HAVE TO CLONE TO NOT THROW EXCEPTION.
return $b;
```
Thanks, I can see how that solves a lot of the same problems, in a very
robustly analysable way.
However, from a high-level user-friendliness point of view, I think
"withX" methods are actually more natural than explicitly cloning
mutable objects.
Consider the case of defining a range: firstly, with plain integers and
familiar operators:
$start = 1;
$end = $start + 5;
This models integers as immutable values, and + as an operator which
returns a new instance. If integers were mutable but not aliasable, we
would instead write something like this:
$start = 1;
$end = clone $start;
$end += 5; // where += would be an in-place modification, not a
short-hand for assignment
I think the first more naturally expresses the desired algorithm. It's
therefore natural to want the same for a range of dates:
$start = MyDate::today();
$end = $start->withAddedDays(5);
vs
$start = MyDate::today();
$end = clone $start;
$end->addDays(5);
To put it a different way, value types naturally form *expressions*,
which mutable objects model clumsily. It would be very tedious if we had
to avoid accidentally mutating the speed of light:
$e = (clone $m) * ((clone $c) ** 2);
The guarantee in both above snippets is that myfun() DOES NOT modify
$b before returning it. BUT with immutability, you have to copy $b
three times, with uniqueness only one.
I wonder if that difference can be optimised out by the
compiler/OpCache: detect clones that immediately replace their original,
and optimise it to an in-place modification. In other words, compile
$foo = clone $foo with { x: 42 } to $foo->x = 42, even if the clone is
actually in a "withX" method.
Regards,
--
Rowan Tommins
[IMSoP]
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php