On Mon, Jun 7, 2021, at 4:06 AM, Nikita Popov wrote: > On Sat, Jun 5, 2021 at 6:51 PM Larry Garfield <la...@garfieldtech.com> > wrote:
> > Thank you for the detailed analysis in the rationale section. I am, > > however, still skeptical of this approach, for a couple of reasons. > > > > 1. This does appear to address the "leakage" problem I noted in my earlier > > analysis around the start of the year, when considering the writeonce > > proposal[1][2]. That's great to see. > > > > 2. It doesn't address the larger question of cloning, however. The answer > > for now seems "maybe we'll get clone-with at some point", which would be a > > way around it, but there is no active RFC for that right now. I'm > > obviously very in favor of RFCs that complement each other to give more > > than the sum of their parts, but those RFCs need to be at least on the > > horizon to actually come together. Right now that looks like it won't > > happen this cycle. Absent clone-with, readonly would be effectively > > unusable in any evolvable object of any non-trivial complexity. > > > Compared to the general area of applicability of readonly properties, the > cases that require clone-with are rare. It's the intersection of > "not-really-immutable objects using wither evolution" and "has so many > properties that passing them to the constructor is infeasible". While this > intersection does include a couple of prominent examples, they are by no > means numerous. While it may be a problem worth solving, I *personally* > don't consider it neither particularly important nor time critical. > > For what it's worth, I believe Mate wants to work on clone-with once this > RFC passes -- while there is no RFC for that, there is already an > implementation, though from a cursory look it would require some > adjustments to actually work with readonly properties. I look forward to it. I don't think the use cases for non-trivial structs (where a withX() method can just return new static(list all properties here) ) are as few as you think. One difficulty in determining that is that, if they are made easier/more robust (via any of the mechanisms under discussion), their usage is likely to increase. That's a good thing, and a benefit of all of these pieces floating around, but it does make it harder to gauge the net-use of any particular feature. I prefer to err on the side of enabling more flexibility and power rather than less. > > It also wouldn't work with objects that need properties that are not > > constructor arguments, such as PSR-7 type objects. That's a no in my book. > > > > I don't understand what you mean here. Why would properties that are not > constructor arguments be a problem? Without clone-with, if you wanted a with-er method, it would necessarily look like this: class Point { public function __construct( public readonly int $x, public readonly int $y, public readonly int $z, ) {} public function withX(int $newX) { return new static($newX, $this->y, $this->z); } } That is, you're populating the entire property set via the constructor. In some cases that's fine; the larger the object, though, the more needlessly verbose redundancy that has. Now let's add a "rendered" flag property, that is public readonly bool. (Maybe not the best example; it's before 9 am and I'm trying to think on the fly here.) It's not in the constructor, but for later processing. It would get set as a side effect of some other method, to indicate the point has already been drawn. Now make a new version of the object with a different Z. What do you do with $rendered? > > 3. As noted in my previous analysis, even with clone-with, asymmetric > > visibility results in a nicer syntax when evolving objects that have any > > complexity to them. (See the examples in [2].) > > > > I tend to agree. And I am not opposed to having asymmetric visibility as > well. For example, C# does both, and I think both do have value. "readonly" > is a very strong signal to the reader. Asymmetric visibility is not. > Asymmetric visibility (without proper readonly support) could mean that the > property is actually readonly, or it could mean that it's exactly what it > says on the tin: The property is internally mutable. I don't think these > two concepts should be conflated, though they certainly have overlap. I think this is the crux of the disagreement. There's a large swath of cases that could be well-handled by either. I feel the cases that are only or better handled by asymmetric visibility is larger than the cases that are only or better handled by readonly. As noted above, I don't know that either of us have any data with which to determine which is actually the case. (Maybe someone with more C# experience can speak to that? What's typical there, where both mechanisms are available?) > > 5. I would have to experiment a bit with hydration, as others have noted, > > because unconventional object construction mechanisms are a critically > > important workflow. The RFC even notes in passing that serialization is > > possibly made complicated. Just how complicated? There's no mention of > > __deserialize() and how it would interact, but cases like that need to be > > very clearly handled and documented. > > > > The RFC notes in passing that *if* readonly default values were allowed, it > could have impact on userland deserialization. As-is, I believe the > proposal doesn't present any problem for userland hydrators or > deserializers, or PHP's own unserialization mechanisms. This should get > mentioned in the proposal, though it will be along the lines of "yes, > everything works as expected". > > > > 6. I'm OK with the approach to constructor promotion and default values. > > That seems reasonable in context, especially if combined with > > New-in-initializers[3], which I am hoping you plan to finish this cycle. :-) > 8. Although the RFC says it does not preclude accessors or explicit > > asymmetric visibility in the future, and I absolutely believe that Nikita > > is genuine about that, I am still concerned that should more robust > > proposals come forward later, it will be met with "we don't need another > > still-fancier syntax here, readonly is good enough." It's good to know > > that C# manages to have both, but that doesn't mean the same logic would > > apply in PHP, or to PHP voters, specifically. > > > > To clarify, I think your argument here goes roughly like this (correct me > if I'm wrong): If we already have one of readonly properties or asymmetric > visibility, an argument could be made for not adding the other one because > it doesn't provide sufficient marginal value. So, if we assume that we can > only have one of these features, is readonly properties the right one to > add? Shouldn't we add asymmetric visibility instead, which can be used for > a wider range of use cases? That is essentially correct. I know you're not making that argument, and I'm not making that argument, but it's an argument I can absolutely see being made, because it has been in the past for other things, and sometimes I agree with it as it can be a valid argument. Hence why, in general, I prefer to go with the more robust, more "interacts nicely with other things" option from the get-go, to both not make more robust options harder to adopt in the future and to avoid user confusion down the road when they have 3 different tools that all do *mostly* the same thing and aren't sure of the subtle reasons why they'd use one over the other. (Like, for example, if one had a readonly property but wanted to convert it to a custom accessor property... what are the subtle knock on effects they have to account for, then? There are almost certainly more of them than if switching a property from implicit accessors to explicit accessors, although off hand I don't know what they would all be.) > I think this is a legitimate concern to have, but (under the assumption > that we can have only one) it's not easy to say which would be preferable. > Yes, asymmetric visibility is more widely applicable, but at the same time > I would expect most applications to be bonafide readonly properties. So > while asymmetric visibility is more widely applicable, it's also imprecise > for the majority use case. Which is better? Of course, my assumption here > could also be wrong. As above, I'd love to get some actual data from the C# community on this. Lacking that, we're all just guessing. > 9. I know that the variance for properties in child classes was a source of > > discussion, and the end result seems to be that readonly-ness is > > invariant. However, that precludes having an easy way to have both a > > mutable and immutable version of a class, easily. (If you wanted to have, > > say, a read-only class most of the time for safety, but for writing you use > > an alternate "open" version that can be updated and then persisted to the > > database.) That's a style I've experimented with on and off for a while > > and already don't have a great solution to, but it feels like readonly > > would make that even harder. Again, I'd be very happy to hear alternatives > > around that. > > > > I don't believe you can't have an immutable class extend a mutable one or a > mutable extend an immutable one, both result in LSP violations (though not > on the type level). There was a lot of discussion around this when > DateTimeImmutable was introduced, which notably is not in an inheritance > relationship (in other direction) with DateTime, for good reason. True. Might that be feasible with asymmetric visibility, however? (I'm honestly not sure.) > > Depending on the answers to the above, I could be convinced of this as a > > stop gap iff paired with clone-with in the same version. However, on its > > own I think this is only a half-solution, and I'm not wild about a > > half-solution for a version or two. That's why I prefer going straight to > > asymmetric visibility, as that would cover mostly the same use case in a > > single RFC while still being forward compatible; it would benefit from > > clone-with, but still be usable without it. > > > > Just to reiterate this point: Readonly properties is not a half solution to > which asymmetric visibility is the full solution. They are *different* > solutions to different albeit overlapping problems. I'm not proposing > readonly properties here because it's the only thing I can get into 8.1, > I'm proposing it because I think it's the more important problem to solve. I was perhaps unclear here. I don't think readonly is a half-solution and asymmetric visibility is the full solution. I think readonly without clone-with is a half-solution. readonly plus clone-with is a mostly-full solution that ends up in a very similar (competing?) place with asymmetric visibility, which is a mostly-full solution. I can probably still vote for readonly plus clone-with, because that combination would be useful. It's readonly without clone-with that I am concerned about being a half-solution. (clone-with is a nice-to-have with asymmetric visibility, but IMO is an ergonomic necessity with readonly.) --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php