On Tue, Nov 19, 2024, at 5:56 AM, Michał Marcin Brzuchalski wrote: > Hi Larry, > > niedz., 17 lis 2024 o 08:24 Larry Garfield <la...@garfieldtech.com> > napisał(a): >> ... >> I can see the benefit of an inline constructor. Kotlin has something >> similar. But I can see the benefit of it for all classes, even service >> classes, not just records. (In Kotlin, it's used for service classes all >> the time.) > > What visibility would you expect for inline constructor properties in > service classes?
Good question. If we follow Kotlin's lead[1] (Kotlin has like 3-4 different places to do constructor-ish logic, it's kinda weird), then they'd default to public, but you can optionally specify a visibility. So, the same as any other property. They do not imply readonly, but Kotlin has per-arg val/var markers on everything anyway to handle that. [1] https://kotlinlang.org/docs/classes.html#constructors >> I would far prefer assembling record-ish behavior myself, using the smaller >> parts above. Eg: >> >> final readonly data class Point(int $x, int $y); >> >> "final" prevents extension. "readonly" makes it immutable. "data" gives it >> value-passing semantics. Any class can use an inline constructor. "with" >> is designed to work automatically on all objects. Boom, I've just assembled >> a Record out of its constituent parts, which also makes it easier for others >> to learn what I'm doing, because the features opted-in to are explicit, not >> implicit. > > It opens a bunch of concerns, questions like why would you use "data" > keyword without having "final"? > Although I like the "data" keyword very much. To borrow a little syntax from Kotlin again, and randomly spitball: data class Rectangle(int $h, int $w) { public int $area { get => $this->h * $this->w; } } data class Square(int $side) extends Rectangle($side, $side); (That calls the constructor of Rectangle with the provided values.) I'm generally not in favor of final-by-default. final has its place, certainly, but I am firmly on team "no, you should not be making every class final by default, that's just silly." Another thing to consider is ADTs. (cf https://wiki.php.net/rfc/tagged_unions, though the text there is quite old and out dated at this point; do not take literally.) The intent for enum cases with associated data was that you would specify properties and types, and those would be public-readonly-enforced. That's the only option. Ilija also had plans (and I think implementation, but I'm not sure) for reusing instances with matching properties so they would point to the same object in memory, making === work. So: enum Move { case Left(int $distance); case Right(int $distance); } $step1 = Move::Left(5); $step2 = Move::Left(5); $step1 === $step2; // true That sounds somewhat similar to the restrictions proposed for Records, though in context I think they make more sense on enums. I can see there being overlap, though, so I mention it here for completeness. I'm not sure how this all dovetails together. To reiterate, my main issue with the Records concept is that it couples too many features together into an all-or-nothing package. readonly did that with only 2 features, and it caused Ilija and I a lot of heartburn and helped keep aviz from passing in 8.3. Records would couple 4 features together. Once bitten, twice as cautious. --Larry Garfield