Re: [RFC[ Property accessor hooks, take 2
| From: | Ilija Tovilo | 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