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