On Wed, Jun 17, 2026, at 4:32 PM, Rob Landers wrote:
> Hello internals,
>
> I was reminded of my records RFC today, and one of the features of the
> RFC was "short constructors" generally called "primary constructors" in
> C#/Kotlin.
>
> They would look like this:
>
> class Point(public int $x, int $id = 0) extends Base($id);
>
> Which is just sugar for this:
>
> class Point extends Base {
> public function __construct(public int $x, int $id = 0) {
> parent::__construct($id);
> }
> }
>
> or this:
>
> class Point extends Base {
> public int $x;
> public function __construct(int $x, int $id = 0) {
> $this->x = $x;
> parent::__construct($id);
> }
> }
>
> A class with a primary constructor *may not* have a defined
> `__construct` function. Any special initialization must be done with
> hooks:
>
> class Temperature(
> public float $celsius {
> set {
> if ($value < -273.15) {
> throw new ValueError('below absolute zero');
> }
> $this->celsius = $value;
> }
> }
> ) {}
>
> new Temperature(20.0); // ok
> new Temperature(-300.0); // ValueError: below absolute zero
>
> I'm sending this email to the list to gather additional feedback before
> pursuing a formal RFC proposal.
>
> — Rob
I would support this, and I agree that KISS is the best way to go for now.
I don't know if it makes sense to include in the initial design, but for
reference here's how Kotlin handles construction:
class Person(val name: String, val age: Int) { // Primary constructor,
equivalent to promotion and what's proposed here.
init {
// Argument-less code block/method that runs after the primary constructor
is assigned.
// This could be useful to include for more complex cases, like where hooks
are useful.
}
// Secondary constructor
constructor(name: String): this(name, 0) {
// This constructor gets called first, but is forced to call the primary
constructor syntactically.
// Then it can do other stuff if it wants.
}
}
cf: https://kotlinlang.org/docs/classes.html#constructors-and-initializer-blocks
I don't think we should include the "secondary constructor calls primary
constructor and you have to make sure it works" logic. However, I do think we
can/should consider an init block. Though how that interacts with inheritance
I'm not sure yet.
Putting non-trivial property hooks inside a constructor promotion is already
strongly discouraged, and I would recommend continuing to discourage them even
with this syntax. However, that does raise a concern that we would really
benefit from some way to allow property hooks to be defined "on their own" on a
property, AND link the property to a promoted constructor argument. I'm not
sure off hand how we would do that, and it's somewhat tangential to this
syntax, but I am pointing it out for completeness.
As for the readonly/hooks incompatibility, honestly, I don't care. :-)
Readonly is frequently problematic, Internals has already rejected making
readonly and hooks compatible, and `protected(set)` gets you almost the same
rules with none of the conflicts. So I'm good there.
--Larry Garfield