On 30.06.26 23:55, Larry Garfield wrote:
On Mon, Jun 29, 2026, at 3:56 PM, Nick Sdot wrote:

     final readonly class Foo(Collection|array $value) implements Bar {
         $this->items = $value instanceof Collection ? $value : new
Collection($value);
     } => {
         public Collection $items;

         // class body private Collection $items
     }

     final readonly class Foo implements Bar {
         public Collection $items;

         public function __construct(Collection|array $value) {
             $this->items = $value instanceof Collection ? $value : new
Collection($value);
         }

         // class body private Collection $items
     }

I find the latter *much* clearer.
Great, then you are lucky, because you *can* write it that way. Now
allow me too writing it how I want writing it and everyone is happy! <3
Best regards
Tim Düsterhus
--

Cheers
Nick
(Not sure where in the thread to put this, so just replying to the most recent 
message.)

It's worth noting that in C# (not sure about the other languages) it's possible 
to set the default value of a non-promoted property to an expression that 
includes the primary constructor parameters.  The example from the C# docs[1]

public struct Distance(double dx, double dy)
{
     public readonly double Magnitude => Math.Sqrt(dx * dx + dy * dy);
     public readonly double Direction => Math.Atan2(dy, dx);
}

Which in PHP would look something like this:

class Distance(float $dx, float $dy)
{
     public readonly float $magnitude = sqrt($dx * $dx + $dy * $dy);
     public readonly float $direction = atan2($dy, $dx);
}

I'm not saying we should adopt that approach, necessarily.  I'm just noting it as a 
possible workaround for "i want a primary constructor and a tiny bit of 
post-processing", if an init block isn't an acceptable.  (I'm not sure how exactly 
that overlaps with property accessors; maybe it's the same thing?)

It sounds like Nick and Tim are fundamentally split on having an init block 
(however it's spelled).  I'm impartial.  I can see the use for it, but I also 
would be OK if it is punted for later.  I'd also be OK with exploring a 
C#-style approach as above.

As far as size/scope, RFCs should be "self-complete."  They should offer some 
functionality that is useful on its own, even if it gets multiplied by some other RFC.  Sometimes 
that means split it up, sometimes it means combine it.  Right-sizing an RFC is not about making it 
as small as possible or as far-reaching as possible.  So if the result of the discussion is that we 
do need to double the length of the RFC to include a constructor init block of some kind, so be it. 
 That's not a bad thing if the result is a better feature.  Nor is leaving it out bad if the result 
is a better feature.  The goal should be "a complete and robust feature", not big or 
small chunks.

[1]https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/primary-constructors#create-mutable-state

--Larry Garfield


Thanks, Larry.
I'd like to take the chance to address Rob's "would explode the complexity of the RFC", because I honestly don't see it. I keep the following examples focused on the parent constructor calling; omitting any unrelated noise.


```

final readonly class Dog(
    string $name,
)extends Animal {
    parent::__construct($name);
}=> {
    // class body }

```

The above:
- does NOT have a parent constructor shortcut call -> `extends Animal`
- DOES allow parent constructor call in primary constructor body
- behaves exactly (!) as a conventional constructor; in fact syntax sugar

```

final readonly class Dog(
    string $name,
)extends Animal($name)// already IS the parent constructor call... {
    // hence, here no parent constructor call allowed }=> {
    // class body }

```

The above:
- DOES have a parent constructor shortcut call -> `extends Animal($name)`
- does NOT allow parent constructor call in primary constructor body
- *no difference in parent constructor calling behaviour to what the RFC proposes now*

It's easy to reason about with: "either parent constructor OR parent constructor shortcut can be called".

It does not need a new name like "init block", it's a constructor body on the primary constructor. Which works as any other conventional constructor body, because it is syntax sugar. So where is the complexity? What would justify not allowing the body that helps to address a real problem?

--

Cheers
Nick

Reply via email to