On 30/01/2015 13:07, Alexander Lisachenko wrote:
What is wrong here? Emulated immutability. All methods will create a
separate instance of request, so
$baseRequest->withUri()->withMethod()->withHeader()->withBody() will create
total 5 object instances. It's a memory overhead and time consumption for
each method call.

Like Andrea, I don't see how immutable variables/objects solve this problem. The overhead is not from emulating the immutability, it is a consequence of the design pattern choosing immutability. In fact, from the code shown, there is no way to know that immutability is in any way relevant, since the definition of withUri() could be a mutator which ends "return $this", a pattern I've frequently used to create such "fluent" interfaces. Copy-on-write doesn't help, either, since all 5 calls are writes, so will still make 5 copies.

What I want to discuss is true immutability flag for variables and
parameters. There are a lot of languages that use "final" or "const"
keywords to prevent modification of variables.

On the concrete suggestion, though, I do think there are possible advantages to this. I've long felt the confusion people have over pass-by-value for primitives, pass-by-value-which-is-actually-a-pointer for objects, and pass-by-reference for a variable which might or might not be an object pointer, is a failure not of the programmer but of the programming language. In a blog post about it a few years ago [1], I suggested that deep immutability (along with deep cloning) could provide a better framework than by-value vs by-reference in modern OO languages.

This is rather different from defining a *type* that is immutable, since it implies temporary immutability of a particular instance; but it seems to be what at least some of your examples are hinting at.

The problem is that deep cloning and deep immutability are non-trivial; PHP notably doesn't support deep cloning of objects, requiring each class to define what to do with any non-primitive members, since some may represent resources which can't be meaningfully cloned just by copying data in memory.

In the same way, in order to make an instance deeply immutable, you need to nail down the details of which actions are actually allowed while it's in that state, and that gets complicated:

- Directly assigning to a public property is clearly invalid, as is calling a method on that property which mutates it. - If the property is itself an object, assigning it to a temporary variable must retain the immutability - that is, we don't want "$foo->bar->setValue(42);" to behave differently from "$x = $foo->bar; $x->setValue(42);" - Calling a method which internally performs such an action is also invalid (like the setValue() in the example above). This could be achieved by executing the function but marking $this as immutable, but that means that the method may have other effects up to the point where the violation occurs, so it would be preferable to somehow invalidate the method call. - For objects which represent proxies for external resources, there may be methods which mutate that external state, and properties which exist only as virtual getters. So calling $db->insertRow(...) should probably be invalid, since it changes the value of $db->lastInsertedId.

I think the only way to do it would be for every method to be invalid unless it is explicitly marked as "immutable-safe", e.g.

class Foo {
private $delegatedObject;
public function __construct() { $this->delegatedObject = new SomethingElse; } immutable public function getValue() { return $this->delegatedObject->getX(); } public function setValue($newValue) { echo "Setting!"; $this->delegatedObject->setX($newValue); }
}
const $foo = new Foo;
echo $foo->getValue();
$foo->setValue(42); // Instantly fails without echoing "Setting!", but method is illegal in immutable context

Copy-on-write, at the user object level, requires both this *and* deep cloning:

$bar = clone-on-write $foo;
$bar->setValue(42); // Needs to implicitly clone $foo before calling setValue(), but must also clone $delegatedObject for that to be meaningful

So class Foo needs an implementation of __clone() as well before any of this can be used, and if it is missing, the resulting behaviour may be rather non-obvious.

As ever, the devil's in the detail. The only language I know of that's meaningfully tackled this is Rust, with its concepts of "boxes" and "lending", although I'm hazy on the details.

[1]: http://rwec.co.uk/blog/2010/08/object-references-are-confusing/

Regards,

--
Rowan Collins
[IMSoP]


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to