Concept: constructor overloading syntax sugar

php.internals

Viktor Khramov

1 year ago
Hi! The point is here: https://gist.github.com/vhood/665418835e65be26d5a818fded92ab75 Is it possible? May I create an RFC for this?

Alexandru Pătrănescu

1 year ago
Hi Viktor, On Mon, Feb 17, 2025 at 4:42 PM Viktor Khramov <dev.999.victor@gmail.com> wrote:
> Hi! > > The point is here: > https://gist.github.com/vhood/665418835e65be26d5a818fded92ab75 > > Is it possible? May I create an RFC for this? >
Already possible like this: class Decision { #[Main] public function __construct( private readonly Uuid $id, private readonly string $comment, ) { } public static function fromEmail(string $mailSubject, string $mailBody): static { $id = preg_replace('/pattern/', "$1", $mailSubject); $comment = preg_replace('/pattern/', "$1", $mailBody); return new static( id: new Uuid($id), comment: $comment, ); } } Related to overloading, that is binding to the correct function in Java/C# at compile time, and IMO is not matching with the dynamic nature of PHP. I think we're not losing much by not having this feature in PHP, but feel free to think more about what the implementation would look like. -- Alex

Tim Düsterhus

1 year ago
Hi
> Related to overloading, that is binding to the correct function in > Java/C# > at compile time, and IMO is not matching with the dynamic nature of > PHP.
Indeed. See: https://github.com/php/php-src/issues/17813 Best regards Tim Düsterhus

Rowan Tommins [IMSoP]

1 year ago
On 17 February 2025 14:39:42 GMT, Viktor Khramov <dev.999.victor@gmail.com> wrote:
>Hi! > >The point is here: >https://gist.github.com/vhood/665418835e65be26d5a818fded92ab75
>Static functions look awful and break the object's API.
Personally, I think quite the opposite: named constructors make a lot more sense than type-based overloads. For instance, you might have both createFromJson and createFromYaml; their inputs are both going to be "string" as far as the type system is concerned, so overloading would be useless. Swift gets around this using "argument labels" which is sort of like requiring named arguments and despatching based on those names, but I believe is actually derived from SmallTalk's multipart method names. Delphi is more explicit: a constructor is any class method marked with "constructor" in its declaration, and the name "Create" is just a convention. It does also support overloading on types, so other names are rarely used, which is a shame. In your example, instead of this meaning different things based on what $a happens to hold: $decision = new Decision($a, $b); You could write one of these to be clear: $decision = Decision::createFromId($a, $b); $decision = Decision::createFromMail($a, $b); Even if someone were to come up with a working implementation of overloading in PHP's type system, I would probably oppose it, because I think it makes code harder to read and reason about. Regards, Rowan Tommins [IMSoP]

Viktor Khramov

1 year ago
Decision::createFromId($uuid, $comment); Decision::createFromMail($subject, $body); Decision::createFromSome($a, $b, $c); Decision::createFromAnother($a); This doesn't look like constructors. It will be hard to read the class, it will take time to come up with a name, it's not right. I have different colors for functions and magic methods in my IDE, __construct is always in focus. new Decision($uuid, $comment); new Decision($subject, $body); This makes it much easier to quickly understand what is going on. вт, 18 февр. 2025 г. в 11:03, Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk>:

Lynn

1 year ago
On Tue, Feb 18, 2025 at 10:27 AM Viktor Khramov <dev.999.victor@gmail.com> wrote:
> Decision::createFromId($uuid, $comment); > Decision::createFromMail($subject, $body); > Decision::createFromSome($a, $b, $c); > Decision::createFromAnother($a); > > This doesn't look like constructors. It will be hard to read the > class, it will take time to come up with a name, it's not right. I > have different colors for functions and magic methods in my IDE, > __construct is always in focus. > > new Decision($uuid, $comment); > new Decision($subject, $body); > > This makes it much easier to quickly understand what is going on. > > вт, 18 февр. 2025 г. в 11:03, Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk > >: > > > > > > > > On 17 February 2025 14:39:42 GMT, Viktor Khramov < > dev.999.victor@gmail.com> wrote: > > >Hi! > > > > > >The point is here: > > >https://gist.github.com/vhood/665418835e65be26d5a818fded92ab75 > > > > > > >Static functions look awful and break the object's API. > > > > Personally, I think quite the opposite: named constructors make a lot > more sense than type-based overloads. For instance, you might have both > createFromJson and createFromYaml; their inputs are both going to be > "string" as far as the type system is concerned, so overloading would be > useless. > > > > Swift gets around this using "argument labels" which is sort of like > requiring named arguments and despatching based on those names, but I > believe is actually derived from SmallTalk's multipart method names. > > > > Delphi is more explicit: a constructor is any class method marked with > "constructor" in its declaration, and the name "Create" is just a > convention. It does also support overloading on types, so other names are > rarely used, which is a shame. > > > > In your example, instead of this meaning different things based on what > $a happens to hold: > > > > $decision = new Decision($a, $b); > > > > You could write one of these to be clear: > > > > $decision = Decision::createFromId($a, $b); > > $decision = Decision::createFromMail($a, $b); > > > > > > Even if someone were to come up with a working implementation of > overloading in PHP's type system, I would probably oppose it, because I > think it makes code harder to read and reason about. > > > > Regards, > > Rowan Tommins > > [IMSoP] >
My only "issue" with static constructors is that it's unclear what you can do, especially if you mix constructors with static constructors. Typing "new TheClass" in PhpStorm for example won't show if the constructor is protected/private based on the scope, and won't show all possible constructors. On the other hand if type `TheClass::` PhpStorm won't show you the normal constructor and none at all if you don't have any static functions. I don't know if there's a good solution to this problem though. Could look into: - `[#Constructor]` attributes - allowing "constructor" instead of "function" when defining the function - these possibly have more benefits such as with-ers and non-static factory methods on other objects While I'd love to have a better way to quickly know what functions are constructors, I don't know if this is something that should be solved in the language.

Sebastian Bergmann

1 year ago
Am 18.02.2025 um 09:00 schrieb Rowan Tommins [IMSoP]:
> named constructors make a lot more sense than type-based overloads
+1

Удальцов Валентин

1 year ago
On Tue, 18 Feb 2025 at 12:45, Sebastian Bergmann <sebastian@php.net> wrote:
> Am 18.02.2025 um 09:00 schrieb Rowan Tommins [IMSoP]: > > named constructors make a lot more sense than type-based overloads > > +1 >
Hi, Viktor. I agree with others that named static constructors are much better than overloading of the primary constructor. Regardless of whether it is possible in PHP or not. Even for Java that supports overloading, Robert Martin recommends factory methods in his book "Clean code": "Prefer static factory methods (with names that describe the arguments) to overloaded constructors." Also see this discussion: https://stackoverflow.com/questions/628950/constructors-vs-factory-methods
-- Valentin

Tim Düsterhus

1 year ago
Hi Am 2025-02-18 09:00, schrieb Rowan Tommins [IMSoP]:
> Personally, I think quite the opposite: named constructors make a lot > more sense than type-based overloads. For instance, you might have both > createFromJson and createFromYaml; their inputs are both going to be > "string" as far as the type system is concerned, so overloading would > be useless.
To add an example from PHP’s standard library: The new DOM API shipped in PHP 8.4 uses named constructors to create documents and requires this to distinguish between `createFromFile` and `createFromString`. https://github.com/php/php-src/blob/da38477544c76c2f034ad74dfd2e1fe613030a31/ext/dom/php_dom.stub.php#L2041-L2045 final class HTMLDocument extends Document { public static function createEmpty(string $encoding = "UTF-8"): HTMLDocument {} public static function createFromFile(string $path, int $options = 0, ?string $overrideEncoding = null): HTMLDocument {} public static function createFromString(string $source, int $options = 0, ?string $overrideEncoding = null): HTMLDocument {} // […] } Best regards Tim Düsterhus

Larry Garfield

1 year ago
On Tue, Feb 18, 2025, at 2:00 AM, Rowan Tommins [IMSoP] wrote:
> On 17 February 2025 14:39:42 GMT, Viktor Khramov > <dev.999.victor@gmail.com> wrote: >>Hi! >> >>The point is here: >>https://gist.github.com/vhood/665418835e65be26d5a818fded92ab75 > > >>Static functions look awful and break the object's API. > > Personally, I think quite the opposite: named constructors make a lot > more sense than type-based overloads. For instance, you might have both > createFromJson and createFromYaml; their inputs are both going to be > "string" as far as the type system is concerned, so overloading would > be useless. > > Swift gets around this using "argument labels" which is sort of like > requiring named arguments and despatching based on those names, but I > believe is actually derived from SmallTalk's multipart method names. > > Delphi is more explicit: a constructor is any class method marked with > "constructor" in its declaration, and the name "Create" is just a > convention. It does also support overloading on types, so other names > are rarely used, which is a shame. > > In your example, instead of this meaning different things based on what > $a happens to hold: > > $decision = new Decision($a, $b); > > You could write one of these to be clear: > > $decision = Decision::createFromId($a, $b); > $decision = Decision::createFromMail($a, $b); > > > Even if someone were to come up with a working implementation of > overloading in PHP's type system, I would probably oppose it, because I > think it makes code harder to read and reason about.
I largely agree. I am open to method overloading as a general feature (which is probably the minority view around here), as I do see places it would be helpful. (Enum methods, operator overloads if those ever happen, etc.) But constructors feel like the least applicable place for them, and named static methods works just fine in my experience. --Larry Garfield