On Tue, Feb 27, 2024, at 00:11, Frederik Bosch wrote:
> Hi Rowan,
> 
> On 26-02-2024 23:46, Rowan Tommins [IMSoP] wrote:
>> On 26/02/2024 20:21, Frederik Bosch wrote:
>>> I do note that $this->propName might suggest that the backing value is 
>>> accessible from other locations than only the property's own get/set 
>>> methods, because of $this usage.
>> 
>> 
>> Yes, I actually stumbled over that confusion when I was writing some of the 
>> examples in my lengthy e-mail in this thread. As I understand it, this would 
>> work: 
>> 
>> public string $foo { 
>>     get { $this->foo ??= 0; $this->foo++; return $this->foo; } 
>>     set { throw new Exception; } 
>> } 
>> 
>> Outside the hooks, trying to write to $this->foo would throw the exception, 
>> because it refers to the hooked property as a whole; but inside, the same 
>> name refers to something different, which isn't accessible anywhere else. 
>> 
>> Now that I've looked more at how Kotlin uses "field", I understand why it 
>> makes sense - it's not an alias for the property itself, but the way to 
>> access a "backing store" which has no other name. 
>> 
>> Using $this->foo as the name is tempting if you think of hooks as happening 
>> "on top of" the "real" property; but that would be a different feature, like 
>> Switft's "property observers" (willSet and didSet). What's really happening 
>> is that we're declaring two things at once, and giving them the same name; 
>> almost as if we'd written this: 
>> 
>> public string $foo { 
>>     get { static $_foo; $_foo ??= 0; $_foo++; return $_foo; } 
>>     set { throw new Exception; } 
>> } 
>> 
>> Kotlin's "field" is kind of the equivalent of that "static $_foo"
>> 
>> 
> And what happens in the following situation, how are multiple get calls 
> working together?
> 
> public string $fullName { 
>     get => $this->first . ' ' . $this->last; // is this accessing the backed 
> value, or is it accessing via get
>     set($value) => $this->fullName = $value;
> }
> 
> public string $first { 
>     get => explode(' ', $this->fullName)[0], // is this accessing the backed 
> value, or is it accessing via get
>     set($value) => $value;
> }
> 
> Isn't it weird that $this->propName gives different results from one get 
> function, compared to the other. I would say $this->prop should always follow 
> the same semantics as explained in the RFC (first __get/__set, then the 
> accessor).
> 
>> 
>>> Regarding returning void=null, this is something that IDE and static 
>>> analyzers already pick-up as an error. I think being stricter on that in 
>>> this RFC would actually make sense, and treat void not as null.
>> 
>> What would happen if a setter contained both "return 42;" and "return;"? The 
>> latter is explicitly allowed in "void" functions, but is also allowed in a 
>> non-void function as meaning "return null;"
> return 42; // returns (int)42
> return; // early return, void, same as no return
> return null; // returns null
>> 
>> 
>>> And why yield is magic, I do not get that. The word and the expression 
>>> actually expresses that something is, well, yielded.
>> 
>> But yielded to where? My mental model of "return to set" is that this: 
>> 
>> public string $name { set($value) { $x = something($value); return $x + 1; } 
>> } 
>> 
>> Is effectively: 
>> 
>> private function _name_set($value) { $x = something($value); return $x + 1; 
>> } } 
>> plus: 
>> $this->name = $this->_name_set($value); 
>> 
>> With "yield", I can't picture that simple translation; the "magic" is 
>> whatever translates the "yield" keyword into "$this->name ="
> You would picture it by explaining how it works from the source side. A set 
> function that contains a yield turns the set function into a directly 
> consumed generator. Considering the following:
> 
> 
> public string $first { 
>     set($value) => {
>         yield 'First name';
>         yield 'Given name';
>         return 'My name';
>     }
> }
> 
> the pseudo-code from the PHP source side would look as follows.
> 
> $generator = setCall($class, 'first', $value);
> foreach ($generator as $value) {
>    writeProperty($class, 'first', $value);
> }
> if ($generator->hasReturn()) {
>   writeProperty($class, 'first', $generator->getReturn());
> }
> 

The yield is much more intuitive than magic fields and $this->prop (which feels 
like an infinite loop). Yield is remarkably simple. 

Looking at this, I'm still not sure what would happen here, though (maybe it is 
covered in the RFC, and I missed it) -- going to use yield here to try it out:

public string $name {
  set => {
    if(strlen($value) < 5) {
      yield 'invalid';
      yield $this->invalidName($value);
    }
    yield $value;
  }
}

public function invalidName($name) {
  return $this->name = str_pad($name, 5);
}

This is probably an infinite loop in this particular example, but more 
importantly, do setters allow reentry while executing?

>> 
>> I would file it with the type widening in the RFC: seems kind of cool, but 
>> probably isn't worth the added complexity. 
>> 
>> 
>> Regards,
> --
> Frederik Bosch
> 
> Partner
> 
> 
> Genkgo logo
> 
> Mail: f.bo...@genkgo.nl
> Web: support.genkgo.com
> 
> Entrada 123
> Amsterdam
> +31 20 244 1920
> 
> Genkgo B.V. staat geregistreerd bij de Kamer van Koophandel onder nummer 
> 56501153

— Rob

Reply via email to