On 09/12/2016 06:47 PM, Stephen Reay wrote:
Ah, I did see that one, but there was a lot of discussion after it so I thought
the idea evolved. Response below based on skimming the responses after that as
well...
It sounds like there's more that needs to go on, though. It sounds like that
thread is suggesting that $this in a method of an immutable object is always
cloned, which seems excessive to me.
The point about identity is well-taken. However, it's more complex on objects
because equality is not as simple as it is on strings. Two objects can be
logically identical but physically different. For instance:
class HttpHeaders {
public $attributes = [];
}
$a = new HttpHeaders();
$a->attributes['foo'] = 'bar';
$a->attributes['baz'] = 'buzz';
$b = new HttpHeaders();
$b->attributes['baz'] = 'buzz';
$b->attributes['foo'] = 'bar';
$a and $b are now not physically identical, since their attributes array is in
a different order. However, header order doesn't matter in HTTP, or rather
isn't supposed to. (Modulo buggy implementations, of course.) So are $a and
$b identical? I could very easily argue both directions on that. Physical
identity would be easier to automate checking in the engine; logical identity
would require user-space code to make such decisions.
From the discussion of "transformer" methods, I'd propose a slightly different
keyword. To wit:
1) A class marked as "immutable" may not have any of its properties altered,
EXCEPT in certain unlocked scopes. The constructor is an unlocked scope.
2) A method may be marked "clone": public clone function foo() {}. A clone method is
identical to any other method except that A) $this in its scope is not the original object, but the
result of clone($this); B) A clone method is an unlocked scope, so modifying $this (the clone) is
legal. That is more self-descriptive than "transformer", and also doesn't require a new
keyword. (By implication, clone and static are mutually exclusive since clone requires an object
to clone.)
I don't know that there are any other unlocked scopes to consider...
#2 does leave us with the identity question that Richard raises, in that
returning an unmodified $this from a clone method still creates a new object
identity. However, I posit that is to be expected. In Python, eg, while some
built-in objects are immutable they are not, necessarily, always the same
physical object in memory. (999+1 is not the same object as 1000, but 1+2 and
2 likely will be due to internal engine optimizations.) You need to do a value
comparison of them.
I don't see the identity case being resolved without an __identity() method, or
similar. Which could be useful in its own right so I'm not necessarily against
it, but it's an extra, and I'd argue optional, piece of the puzzle.
Related to whether or not the properties of an object may be mutable (arrays,
other objects, etc.), they would in practice probably need to blacklist
resources. We ran into that issue in PSR-7, where the body stream has to be
mutable, because streams. Since PSR-7 is based on PHP 5.3 that doesn't cause
any syntactic issues, just logical issues. If the classes were marked
immutable explicitly, it likely would be a problem since the streams cannot be
immutable, thus they can't be used on an immutable object. Which is... very
sad making. :-(
--Larry Garfield
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
I like your suggestion with the ‘clone’ keyword on methods.
As I said before, I think it’s a mis-step to prevent manual cloning at the
engine level (even if the result is that you return the same instance) - there
is *bound* to be user land code that clones objects. Without this, any use of
clone in a distributed library/framework will have to do a check to see if an
object (except those accepting final classes of known definition) is immutable
before attempting to clone it.
I think most people in the thread have agreed that blocking clone() is
unnecessary, the RFC just hasn't been updated yet. With a clone method,
an external clone becomes basically a pointless but harmless operation,
I'd think. A clone within the class would be fairly nonsensical, except
for the clone methods.
Regarding identity, I’m going to refer back to the DateTimeImmutable class. I
know its not the same implementation, but honestly I don’t think that matters.
Developers use PHP because they generally *don’t* have to worry about the
internal details of the engine.
e.g.:
$d = new DateTimeImmutable();
$e = $d->add(new DateInterval('PT0S'));
var_dump($d === $e); // bool(false)
Personally I think if we want to worry about whether two objects represent the same
value, wouldn’t that be better handled (and with much greater positive effect for
developers) by finalising & passing the Comparable RFC
(https://wiki.php.net/rfc/comparable)?
Cheers
Stephen
That does seem like the more targeted solution, yes.
I forgot to mention before, there was discussion of marking interfaces
as immutable. I would argue yes, they should, because you'd want to
mark methods within the class as clone methods. While I suppose that
would technically not require an immutable class/interface, it's the
obvious place to use it.
(Which is another interesting question: is there a reason to not allow
clone methods on non-immutable classes? That seems... I suppose
unnecessary but harmless to allow, so may as well?)
--Larry Garfield
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php