On 01/31/2015 06:55 PM, Stanislav Malyshev wrote:

It is great that this is fast, but I wonder (maybe off-topic?) why do
it? I.e. it is clear that in something like:

$a = new Request->withHeaders(...)->withBody(...)
->withEncoding(...)->withETag(...)

the intermediate objects are useless and nobody needs 5 new objects when
you do it. Am I missing something here?

Primarily for the reasons Andrea listed: Avoid spooky-action-at-a-distance. In the particular case of a request (or ServerRequest), it's very tempting to use as an over-engineered global with all of the problems that causes. When doing sub requests, or recursive requests, any code that relies on a global request object is now invalid and introduces all sorts of weirdness. By not allowing that object to change without the system knowing about it explicitly you eliminate a lot of sources of weird bugs.

(There were several very long threads on the FIG list about mutability and I'm greatly over-simplifying them here. There are also still dissenters who would favor a mutable object, still, although right now the group is leaning immutable.)


On 01/31/2015 08:56 PM, Andrea Faulds wrote:
Hi,

I'd rather just have a clear separation between mutating and
non-mutating APIs, and instruct people to use the right ones in right
situation - i.e. if you created the object or own it, use mutating ones,
if you got object from outside and do not have full ownership of it, use
non-mutating ones.

This isn’t very nice in practice, though. Mutating APIs are easy to use and 
performant, non-mutating APIs are neither of these things. What you want is the 
benefits of the first without the disadvantages of the second.

I disagree here. In the micro-sense, mutating APIs may be easier and more performant at first. At scale, though, immutable APIs tend to be much more predictable and less prone to mysterious behavior.

Drupal 7's render API is an excellent demonstration of how mutable data structures can result in completely incomprehensible, almost non-deterministic code. :-)


Would that make sense? It’s no different than how our existing value
types like scalars and arrays work.
Scalars don't have this problem as, except for string offsets (IMHO not
the best idea to have mutable strings too) scalars can not really be
changed, just replaced with other scalars. But implementing value
objects in PHP is not hard right now - if you don't provide any methods
that allow changing state, you've got an immutable object. It's just not
always what people using it would want, especially with something as
complex as HTTP message.
You’re ignoring arrays, which *also* have our copy-on-write behaviour. Also, 
the bigint RFC (if passed) would be another kind of mutable scalar. It’s not 
mutable in very many cases, but it is in some places as a performance 
optimisation.

We have value objects, sure, but they’re not efficient. Every mutation requires 
the creation of a new object, because you can’t do copy-on-write. Compare that to 
arrays: mutations there only create new arrays if the refcount is > 1.

The following code using immutable value objects requires the creation of five 
new objects:

$a = $somefoo
      ->withBar(…)
      ->withBaz(…)
      ->withQux(…)
      ->withoutFooBar();

Yet the following code using arrays, which are passed by value in PHP, requires 
the creation of only one new array, and modifies in-place:

$a = $somefoo;
$a[‘bar’] = …;
$a[‘baz’] = …;
$a[‘qux’] = …;
unset($a[‘foobar’]);

Is that not superior?

Depends what it is you're prioritizing. Arrays-as-junior-structs only scale so far, and in my experience they break down surprisingly fast. (See above regarding Drupal's Array-Oriented APIs.) They also don't have any natural mechanism to do anything but CRUD, so methods that derive information from provided data, or provide defaults for various values, or do anything to provide a nice developer experience (DX) are impossible. They're also completely non-self-documenting, whereas a class with defined properties and methods, mutable or not, is much easier to learn how to use.

Having some objects that are pass-by-value and others that are pass-by-handle/reference... honestly scares me. The potential for confusion there is huge. Last I recall there was discussion of trying to revamp arrays to be more like objects to finally resolve the "array_*() functions and iterators are incompatible and the world sucks" problem, so that would seem to go the other direction. (Did anyone end up working on that for PHP 7? Please?)

If you really wanted a compound variable (like objects and arrays) that was just for data and passed by value but had a nicer DX than undocumentable array keys... now you're talking about actual structs, and letting them have actor functions a la Go. But I should probably stop talking now before someone shoots me. :-)

--Larry Garfield

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

Reply via email to