> On 14. Jul 2025, at 01:15, Eric Norris <eric.t.nor...@gmail.com> wrote: > > On Sun, Jul 13, 2025 at 2:00 PM Marc Bennewitz <marc@mabe.berlin> wrote: >> >> >> On 13.07.25 18:17, Nick wrote: >> >> >> On 13. Jul 2025, at 20:38, Marc Bennewitz <marc@mabe.berlin> wrote: >> >> Hi Nick, Claude, >> >> Hey Marc, >> >> I think it's important to explicitly mark it as "this value will be stored >> in memory", I mean just silently caching the get hook could quickly lead to >> unexpected behavior. Like one would expect the value to be changed >> >> The most here made the argument that "a changing value from a readonly get >> hook" would be the unexpected behaviour. >> That’s why we ended up with the current “alternative implementation 2”. >> Please see my last answer to Rob for a fair example [1] . >> >> The current preferred alternative implementation covers both situations... >> >> If a property is `readonly`: >> - you can set once >> - on read you always get the same (once computed) value back >> >> This is exactly the behavior I mean which is somehow unexpected if not >> marked explicitly as the result could be cached and the property value will >> never change. >> >> Not being able to write to something doesn't generally mean reading the >> value will never change. `get => random_int(0, 100);` >> >> I don't want to say that the path we want to go with readonly being able to >> assume the value will never change is wrong but I think it's not clear for >> the user and can be missed quickly - even with a test as you have to read >> the property multiple time to notice the difference. >> >> >> If a property is NOT `readonly`: >> - you can set often >> - on read you always get the fresh (often computed) value back >> >> I argue that this is a very easy mental model. >> I hope that voters agree on “cached may be implied by readonly”, as Claude >> called it. >> >> All what I'm saying is that this behavior should be explicit and not applied >> implicitly on a readonly get hook. > > I agree with Marc here, not surprisingly. > >> To have an `init` hook doesn’t solve the get hook issue. >> >> As far as I understood the init hook it would still disallow readonly+get >> but allow readonly+init and init would be called once the first time the >> property gets read and the result would end up in the backing store. >> >> As a result you get the same behavior as `cached get` with the only >> difference that you write `init => random_int();` instead of `cached get => >> random_int();` > > Agreed. Nick, I am not sure how the init hook doesn't "solve the get > hook issue". As I understand it, the get hook issue is that get hooks > are not allowed on readonly properties. The init hook would be > identical to a "cached get" hook on a readonly property, so why > doesn't it solve the issue? > >> Or did I misunderstand it? > > I share the same understanding as you, Marc. > >> The cached modifier I would expect to be an attribute applicable to any >> function which uses another cache store similar to how it's possible in >> python to memorize function calls which would be a very different feature. >> >> As earlier answered to Claude [2], I seek to write less code. To introduce a >> `cached` modifier voids this for no strong reason (please see “mental model” >> above). >> >> See above - and `init` is just once more character. > > I was going to respond to this point earlier, but Marc beat me to it. > An "init" hook is one more character than a get hook, is explicit over > a get hook that works differently only for readonly properties, *and* > is far fewer characters than the explicit "cached" modifier get hook > option. > > On top of that, as Claude mentioned an init hook provides the ability > to differentiate between a null property and an uninitialized property > - an init hook would only be called for uninitialized properties, so > no need for $this->foo ??= "bar”.
Hey Marc, Hey Eric, A) Init hook Marc, > Or did I misunderstand it? Well, I don’t know. Everyone seems to think of init hooks (and their playing together with other hooks) differently. Some say this, some say that. That’s the exact issue. Want an example? Eric just agreed with your code example which has a get hook AND init hook. >> ``` >> class Test { >> public int $seek { >> init => random_int(0, 100); // called once on read if not >> initialized >> get => $this->seek + 100; >> set => $this->seek = $value + 100; >> } >> } >> ``` >> var_dump((new Test())->seek); // number between 100-200 OR 200-300 depending >> of the set hook be called once with the result of init as well. >> Or did I misunderstand it? > > I share the same understanding as you, Marc. But one mail before he answered to Larry: > I think, at least for readonly, you couldn't have an init hook and a > get hook, since the main objection here is to having get hooks on > readonly properties at all. On normal properties, I think that'd be > okay? So what is it? Get hook cool, or not? And how does an init hook work exactly? How play all combinations together? And how with readonly? Why didn’t your example use readonly if we talk about readonly hooks? I don’t know all that. And that’s why I have proposed what I proposed. We have a reasonable solution for set/get, without init, right in front of us; and millions of devs could benefit from it the next release. Eric, why it wouldn’t be solved by an init hook? Well, because as you said, readonly get hooks would not be allowed in combination with init. Others apparently have different opinions. So will they, will they not? I, however, want get/set on readonly properties. And that is what I proposed here. An init hook is not part of this proposal and I am not planning to take this on. This RFC, however, would not block anyone from creating their own RFC for init hooks. B) Less Code Marc was talking about init, cache modifier and attributes in the same time. Claude initially wanted a cache modifier on each hook. I argue that “readonly implicates cached” is very reasonable here. Many, many opinions. Many, many options. All have their pros, and cons. All can be attacked, and defended. :) All I want is the below (less code; and no dealing with unrelated things just because I want to add hooks to a readonly class). ```php // I have a nice readonly class final readonly class Entry { public function __construct( public string $word, public string $slug, ) {} } // I simply want to add a hooked-property to that readonly class final readonly class Entry { public function __construct( public string $word, public string $slug, public array $terms { set(array $value) => array_map(static function (Term|array $term): Term { return $term instanceof Term ? $term : new Term(...$term); }, $value); get => $this->terms; // something, something }, ) {} } // but I cannot. I need to: // - make the class non-readonly, // - add some readonly here and there, // - deal with async visibility // to eventually end up with this final class Entry // cannot be readonly, annoying { public function __construct( public readonly string $word, // annoying readonly public readonly string $slug, // annoying readonly private(set) array $terms { // requires visibility set(array $value) => array_map(static function (Term|array $term): Term { return $term instanceof Term ? $term : new Term(...$term); }, $value); get => $this->terms; }, ) {} } ``` Adding hook to a readonly class really should not be THAT hard and demanding. And for that, I believe, I provided a solution that is easy to reason about (all details in previous mails), and allows everyone to achieve what they want. Cheers, Nick