Re: [RFC[ Property accessor hooks, take 2

From: Date: Sun, 17 Mar 2024 00:01:21 +0000
Subject: Re: [RFC[ Property accessor hooks, take 2
References: 1 2 3 4 5 6 7 8 9 10 11  Groups: php.internals 
Request: Send a blank email to internals+get-122662@lists.php.net to get a copy of this message
Hi Rowan On Sat, Mar 16, 2024 at 8:23 PM Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote: > > I still think there will be a lot of users coming from other languages, or from using __get and > __set, who will look at virtual properties first. Making things less surprising for those people > seems worth some effort, but I'm not asking for a complete redesign. For clarity, you are asking for a way to make the "virtualness" of properties more explicit, correct? We touch on a keyword and why we think it's suboptimal in the FAQ section. Unfortunately, I cannot think of many alternatives. The $field variable made it a bit more obvious, but only marginally. I do believe that, for the most part, the user should not have to think much about whether the property is backed or virtual. The behavioral differences are mostly intuitive. For example: ```php class Test { // This property has a set hook that writes to the backing value. Since // we're using the backing value, it makes sense for there to be a way to // retrieve it. Without that, it wouldn't be useful. public $prop { set { $this->prop = strtoupper($value); } } // Similarly, a property with only a get hook that accesses the backing // value would need a way to write to the property for the get to be useful. public $prop { get => strtoupper($this->prop); } // A property with a get hook that does not use the backing value does not // need an implicit set operation, as writing to the backing value would be // useless, given that nobody will read it. public $prop { get => 42; } // Similarly, in the esoteric write-only case that does not use the backing // value, having an implicit get operation would always lead to a // "uninitialized property" error, and is not useful as such. public $prop { set { echo "Prop set\n"; } } } ``` Furthermore, serialize, var_dump and the other functions operating on raw property values will include the property only if it is backed. This also seems intuitive to me: If you never use the backing value, the backing value would always be uninitialized, so there's no reason to include it. One case that is not completely obvious is lazy-initialized properties. ```php class Test { public $prop { get => $this->prop ??= expensiveOperation(); } } ``` It's not immediately obvious that there is a public set operation here. The correct way to fix this would be with asymmetric visibility, which was previously declined. Either way, I don't consider this case alone enough to completely switch our approach. Please let me know if you are aware of any other potentially non-intuitive cases. I will admit that it is unfortunate that a user of the property has to look through the hook implementation to understand whether a property is writable. As you have previously suggested, one option might be to add an explicit set; declaration. Maybe it's a bit more obvious now, after my previous e-mail, why we are trying to avoid this. Apart from the things already mentioned, it's unclear to me whether, with such set; declarations, a get-only backed property should even be legal. With the complete absence of a write operation, the assignment within the set itself would fail. To make this work, the absence of set; would need to mean something like "writable, but only within another hook", which introduces yet another form of asymmetric visibility. > > Dynamic properties are not particularly relevant today. The point was > > not to show how similar these two cases are, but to explain that > > there's an existing mechanism in place that works very well for hooks. > > We may invent some new mechanism to access the backing value, like > > field = 'value', but for what reason? This would > > only make sense if > > the syntax we use is useful for something else. However, given that > > without guards it just leads to recursion, which I really can't see > > any use for, I don't see the point. > > I can think of several reasons we *could* explore other syntax: > > 1) To make it clearer in code whether a particular line is accessing via the hooks, or > by-passing them 2) To make the code in the hooks shorter (e.g.. > $field is significantly shorter than > $this->someDescriptiveName) 3) To allow code to by-pass the hooks > at will, rather than only when called from the hooks (e.g. having a single method that resets the > state of several lazy-loaded properties) > > Those reasons are probably not enough to rule out the current syntax; but they show there are > trade-offs being made. Fair enough. 1 and 2 are reasons why we added the $field macro as an alternative syntax in the original draft. I don't quite understand point 3. In Kotlin, field is only usable within its associated hook. Other languages I'm aware of do not provide a way to access the backing value directly, neither inside nor outside the accessor. > To be honest, my biggest hesitation with the RFC remains asymmetric types (the ability to > specify types in the set hook). It's quite a significant feature, with no precedent I know of, > and I'm worried we'll overlook something by including it immediately. For instance, what > will be the impact on people using reflection or static analysis to reason about types? I would > personally be more comfortable leaving that to a follow-up RFC to consider the details more > carefully. I personally do not feel strongly about whether asymmetric types make it into the initial implementation. Larry does, however, and I think it is not fair to exclude them without providing any concrete reasons not to. I will spend time in the following days cleaning up tests, and I will try my best to try to break asymmetric types. If I (or anybody else) can't find a way to do so, I don't see a reason to remove them. > Nobody else has raised that, beyond the syntax; I'm not sure if that's because > everyone is happy with it, or because the significance has been overlooked. Yes, unfortunately that's a classic problem in RFC discussions: Syntax gets a disproportionate amount of attention. Ilija

« previous php.internals (#122662) next »