On 12/03/2024 22:43, Larry Garfield wrote:
It's slightly different, yes.  The point is that the special behavior of a hook is 
disabled if you are within the call stack of a hook, just like the special behavior of 
__get/__set is disabled if you are within the call stack of __get/__set.  What happens 
when you hit an operation that would otherwise go into an infinite loop is a bit 
different, but the "disable to avoid an infinite loop" logic is the same.


I guess I'm looking at it more from the user's point of view: it's very rare with __get and __set to have a method that sometimes accesses the "real" property, and sometimes goes through the "hook". Either there is no real property, or the property has private/protected scope, so any method on the classes sees the "real" property *regardless* of access via the hook.

I think it would be more helpful to justify this design on its own merits, particularly because it's a significant difference from other languages (which either don't have a "real property" behind the hooks, or in Kotlin's case allow access to it only *directly* inside the hook definitions, via the "field" keyword).



The point is to give the user the option for full backwards compatibility when it makes 
sense. This requires jumping through some hoops, which is the point. This is essentially 
equivalent to creating a by-ref getter + a setter, exposing the underlying property. By 
creating a virtual property, we are "accepting" that the two are detached. While we 
could disallow this, we recognize that there may be valid use-cases that we'd like to enable. 
 It also parallels __get/__set, where using &__get means you can write to something 
without going through __set.


I get the impression that to you, it's a given that a "virtual property" is something clearly distinct from a "property with hooks", and that users will consciously decide between one and the other.

This isn't my expectation; based on what people are used to from existing features, and other languages, I expect users to see this as an obvious starting point for defining a hooked property:

private int $_foo;
public int $foo { get => $this->_foo; set { $this->_foo = $value; } {

And this as a convenient short-hand for exactly the same thing:

public int $foo { get => $this->foo; set { $this->foo = $value; } }

Choosing one or the other won't feel like "jumping through a hoop", and the ability to use an &get hook with one and not the other will simply seem like a weird oddity.



In practice I expect it virtual properties with both hooks to be very rare.  
Most virtual properties will, I expect, be lazy-computed get-only values.


I don't think this is true. Both of these are, in the terms of the RFC, "virtual properties":

public Something $proxied { get => $this->otherObject->thing; set { $this->otherObject->thing = $value; } };

public Money $price;
public int $pricePence { get => $this->price->asPence(); set { $this->price = Money::fromPence($value); } }

I can also imagine generated classes with "virtual" properties which call out to generic "getCached" and "setAndClearCache" methods doing the job of this pair of __get and __set methods: https://github.com/yiisoft/yii2/blob/master/framework/db/BaseActiveRecord.php#L274





With the change to allow &get in the absence of set, I believe that would 
already work.

cf:https://3v4l.org/3Gnti/rfc#vrfc.property-hooks


Awesome! The RFC should probably highlight this, as it gives a significant extra option for array properties.

(Also, good to know 3v4l has a copy of the branch; I hadn't thought to check.)


Regards,

--
Rowan Tommins
[IMSoP]

Reply via email to