Re: [PHP-DEV] [RFC][Proposal] Renamed parameters

This is only part of a thread. view whole thread
  111168
July 24, 2020 13:15 t.carnage@gmail.com (Chris Riley)
On Fri, 24 Jul 2020 at 14:01, Benjamin Eberlei <kontakt@beberlei.de> wrote:

> > > On Fri, Jul 24, 2020 at 1:13 PM Chris Riley carnage@gmail.com> wrote: > >> Hi all, >> >> The named parameters RFC has been accepted, despite significant objections >> from maintainers of larger OSS projects due to the overhead it adds to >> maintaining backwards compatibility as it has now made method/function >> parameter names part of the API; a change to them would cause a BC break >> for any library users who decide to use the new feature. >> > > Hi Chris, > > I had something similar in mind, but using an attribute. Here is a patch > that already allows this: > > > https://github.com/beberlei/php-src/commit/4b0a02f9c6ba579f93ec57c754fa3794a96c696b > > Idea: Have a @@NameAlias attribute, where you can provide a second name > for the attribute. This would allow to refactor parameter names by adding > the attribute with the old name as an alias. > > >> >> It is likely that the way this will shake out is that some maintainers >> will >> accept the additional overhead of including parameter names in their BC >> guidelines and others will not, this leaves users unsure if they can use >> the new feature without storing up issues in potentially minor/security >> releases of the libraries they use. This is not really an ideal situation. >> >> More pressing a point is that the current implementation breaks object >> polymorphism. Consider this example (simplified from one of my codebases) >> >> interface Handler { >> public function handle($message); >> } >> >> class RegistrationHandler implements Handler { >> public function handle($registraionCommand); >> } >> >> class ForgottenPasswordHandler implements Handler { >> public function handle($forgottenPasswordCommand); >> } >> >> class MessageBus { >> //... >> public function addHandler(string $message, Handler $handler) { //... >> } >> public function getHandler(string $messageType): Handler { //... } >> public function dispatch($message) >> { >> $this->getHandler(get_class($message))->handle(message: $message); >> } >> } >> >> This code breaks at run time. >> >> Proposals were made for resolutions to this issue however all of them >> require trade offs and could potentially break existing code. >> >> My proposal to resolve these two issues is to add the ability to rename >> parameters with a new syntax as follows. >> >> function callBar(Foo $internalName:externalName) { >> $internalName->bar(); >> } >> >> $x = new Foo(); >> callBar(externalName: $x); >> >> This allows both the above problems to be resolved, by renaming the >> internal parameter and keeping the external signature the same. >> >> I propose that the RFC would have two voting options. >> >> The first would be to implement it as proposed above, this would allow any >> parameter to be called by name regardless of the intentions of the author >> of the method/function and is closest to the current behaviour. >> >> The second option would be to use this syntax to make named parameters in >> userland code explicitly opt in. As such an additional shortcut syntax >> would be implemented: $: to designate a named parameter. eg >> >> function callBar($:externalName) { >> $externalName->bar(); >> } >> >> $x = new Foo(); >> callBar(externalName: $x); >> >> If a parameter is not opted in, a compile time error is raised: >> >> function callBar($externalName) { >> $externalName->bar(); >> } >> >> $x = new Foo(); >> callBar(externalName: $x); // Error: cannot call parameter $externalName >> by >> name. >> >> There are pros and cons to this second approach, on the one hand it >> reduces >> the usefulness of the named parameter syntax by requiring changes to old >> code to enable it (although this could probably be automated fairly >> easily) >> however it does provide a neater solution to the second problem in that, >> to >> prevent the runtime errors in the second issue example, every child class >> would need to use the rename syntax on it's parameter to prevent errors, >> whereas if we went down this route, the parent class could just not opt >> into the named parameter syntax and the code would function as expected. >> >> Another advantage is that with the ability to rename parameters using the >> opt in, we gain some flexibility to tighten up the LSP rules relating to >> named parameter inheritance. >> >> class Foo { >> public function bar($:param) { //... } >> public function baz($internal:external) { //... } >> } >> >> // OK >> class Bar { >> public function bar($renamed:param) { //... } >> public function baz($renamed:external) { //... } >> } >> >> // Compile time error cannot rename named parameter $:param (renamed to >> $:renamedParam) >> class Baz { >> public function bar($:renamedParam) { //... } >> } >> >> // Compile time error cannot rename named parameter $:external (renamed to >> $:renamed) >> class Baz { >> public function baz($internal:renamed) { //... } >> } >> >> While this would be technically possible with the first option (no opt in) >> it would break any existing code which renames a parameter as every >> parameter would be subject to these rules. >> >> I don't have Wiki karma so can't post this yet; but I want to get the ball >> rolling on discussion as feature freeze is coming up fast and if we want >> to >> go for the second option, that must hit before the named parameter syntax >> is in a tagged version of PHP. >> >> Regards, >> Chris >> > Hi,
While the attribute idea does solve the BC problems, I'm not seeing how it really solves the polymorphism issue - could you provide an example of how this would work? In either case; as a personal preference I'd rather stick to syntax that affects the parameter as part of it's definition instead of tweaking it's behaviour with an attribute. It just feels easier to grasp what is going on. Regards, Chris
  111170
July 24, 2020 13:42 kontakt@beberlei.de (Benjamin Eberlei)
On Fri, Jul 24, 2020 at 3:15 PM Chris Riley carnage@gmail.com> wrote:

> On Fri, 24 Jul 2020 at 14:01, Benjamin Eberlei <kontakt@beberlei.de> > wrote: > >> >> >> On Fri, Jul 24, 2020 at 1:13 PM Chris Riley carnage@gmail.com> wrote: >> >>> Hi all, >>> >>> The named parameters RFC has been accepted, despite significant >>> objections >>> from maintainers of larger OSS projects due to the overhead it adds to >>> maintaining backwards compatibility as it has now made method/function >>> parameter names part of the API; a change to them would cause a BC break >>> for any library users who decide to use the new feature. >>> >> >> Hi Chris, >> >> I had something similar in mind, but using an attribute. Here is a patch >> that already allows this: >> >> >> https://github.com/beberlei/php-src/commit/4b0a02f9c6ba579f93ec57c754fa3794a96c696b >> >> Idea: Have a @@NameAlias attribute, where you can provide a second name >> for the attribute. This would allow to refactor parameter names by adding >> the attribute with the old name as an alias. >> >> >>> >>> It is likely that the way this will shake out is that some maintainers >>> will >>> accept the additional overhead of including parameter names in their BC >>> guidelines and others will not, this leaves users unsure if they can use >>> the new feature without storing up issues in potentially minor/security >>> releases of the libraries they use. This is not really an ideal >>> situation. >>> >>> More pressing a point is that the current implementation breaks object >>> polymorphism. Consider this example (simplified from one of my codebases) >>> >>> interface Handler { >>> public function handle($message); >>> } >>> >>> class RegistrationHandler implements Handler { >>> public function handle($registraionCommand); >>> } >>> >>> class ForgottenPasswordHandler implements Handler { >>> public function handle($forgottenPasswordCommand); >>> } >>> >>> class MessageBus { >>> //... >>> public function addHandler(string $message, Handler $handler) { >>> //... } >>> public function getHandler(string $messageType): Handler { //... } >>> public function dispatch($message) >>> { >>> $this->getHandler(get_class($message))->handle(message: >>> $message); >>> } >>> } >>> >>> This code breaks at run time. >>> >>> Proposals were made for resolutions to this issue however all of them >>> require trade offs and could potentially break existing code. >>> >>> My proposal to resolve these two issues is to add the ability to rename >>> parameters with a new syntax as follows. >>> >>> function callBar(Foo $internalName:externalName) { >>> $internalName->bar(); >>> } >>> >>> $x = new Foo(); >>> callBar(externalName: $x); >>> >>> This allows both the above problems to be resolved, by renaming the >>> internal parameter and keeping the external signature the same. >>> >>> I propose that the RFC would have two voting options. >>> >>> The first would be to implement it as proposed above, this would allow >>> any >>> parameter to be called by name regardless of the intentions of the author >>> of the method/function and is closest to the current behaviour. >>> >>> The second option would be to use this syntax to make named parameters in >>> userland code explicitly opt in. As such an additional shortcut syntax >>> would be implemented: $: to designate a named parameter. eg >>> >>> function callBar($:externalName) { >>> $externalName->bar(); >>> } >>> >>> $x = new Foo(); >>> callBar(externalName: $x); >>> >>> If a parameter is not opted in, a compile time error is raised: >>> >>> function callBar($externalName) { >>> $externalName->bar(); >>> } >>> >>> $x = new Foo(); >>> callBar(externalName: $x); // Error: cannot call parameter $externalName >>> by >>> name. >>> >>> There are pros and cons to this second approach, on the one hand it >>> reduces >>> the usefulness of the named parameter syntax by requiring changes to old >>> code to enable it (although this could probably be automated fairly >>> easily) >>> however it does provide a neater solution to the second problem in that, >>> to >>> prevent the runtime errors in the second issue example, every child class >>> would need to use the rename syntax on it's parameter to prevent errors, >>> whereas if we went down this route, the parent class could just not opt >>> into the named parameter syntax and the code would function as expected. >>> >>> Another advantage is that with the ability to rename parameters using the >>> opt in, we gain some flexibility to tighten up the LSP rules relating to >>> named parameter inheritance. >>> >>> class Foo { >>> public function bar($:param) { //... } >>> public function baz($internal:external) { //... } >>> } >>> >>> // OK >>> class Bar { >>> public function bar($renamed:param) { //... } >>> public function baz($renamed:external) { //... } >>> } >>> >>> // Compile time error cannot rename named parameter $:param (renamed to >>> $:renamedParam) >>> class Baz { >>> public function bar($:renamedParam) { //... } >>> } >>> >>> // Compile time error cannot rename named parameter $:external (renamed >>> to >>> $:renamed) >>> class Baz { >>> public function baz($internal:renamed) { //... } >>> } >>> >>> While this would be technically possible with the first option (no opt >>> in) >>> it would break any existing code which renames a parameter as every >>> parameter would be subject to these rules. >>> >>> I don't have Wiki karma so can't post this yet; but I want to get the >>> ball >>> rolling on discussion as feature freeze is coming up fast and if we want >>> to >>> go for the second option, that must hit before the named parameter syntax >>> is in a tagged version of PHP. >>> >>> Regards, >>> Chris >>> >> > Hi, > > While the attribute idea does solve the BC problems, I'm not seeing how it > really solves the polymorphism issue - could you provide an example of how > this would work? > > In either case; as a personal preference I'd rather stick to syntax that > affects the parameter as part of it's definition instead of tweaking it's > behaviour with an attribute. It just feels easier to grasp what is going on. >
Attributes are part of the declaration from PHP 8 on, and make them prime implementation device for this kind of functionality, because they don't require new keywords (same goes for ReadOnly and many other proposals that were shot down leading up to PHP 8). You are right about poloymorphism, that would "only" work when every implementation adds @@NameAlias("message"). You could devise another attribute for this that gets inherited: @@InheritName or something (with probably a better name needing to be found).
> Regards, > Chris >
  111171
July 24, 2020 13:58 t.carnage@gmail.com (Chris Riley)
On Fri, 24 Jul 2020 at 14:43, Benjamin Eberlei <kontakt@beberlei.de> wrote:

> > > On Fri, Jul 24, 2020 at 3:15 PM Chris Riley carnage@gmail.com> wrote: > >> On Fri, 24 Jul 2020 at 14:01, Benjamin Eberlei <kontakt@beberlei.de> >> wrote: >> >>> >>> >>> On Fri, Jul 24, 2020 at 1:13 PM Chris Riley carnage@gmail.com> wrote: >>> >>>> Hi all, >>>> >>>> The named parameters RFC has been accepted, despite significant >>>> objections >>>> from maintainers of larger OSS projects due to the overhead it adds to >>>> maintaining backwards compatibility as it has now made method/function >>>> parameter names part of the API; a change to them would cause a BC break >>>> for any library users who decide to use the new feature. >>>> >>> >>> Hi Chris, >>> >>> I had something similar in mind, but using an attribute. Here is a patch >>> that already allows this: >>> >>> >>> https://github.com/beberlei/php-src/commit/4b0a02f9c6ba579f93ec57c754fa3794a96c696b >>> >>> Idea: Have a @@NameAlias attribute, where you can provide a second name >>> for the attribute. This would allow to refactor parameter names by adding >>> the attribute with the old name as an alias. >>> >>> >>>> >>>> It is likely that the way this will shake out is that some maintainers >>>> will >>>> accept the additional overhead of including parameter names in their BC >>>> guidelines and others will not, this leaves users unsure if they can use >>>> the new feature without storing up issues in potentially minor/security >>>> releases of the libraries they use. This is not really an ideal >>>> situation. >>>> >>>> More pressing a point is that the current implementation breaks object >>>> polymorphism. Consider this example (simplified from one of my >>>> codebases) >>>> >>>> interface Handler { >>>> public function handle($message); >>>> } >>>> >>>> class RegistrationHandler implements Handler { >>>> public function handle($registraionCommand); >>>> } >>>> >>>> class ForgottenPasswordHandler implements Handler { >>>> public function handle($forgottenPasswordCommand); >>>> } >>>> >>>> class MessageBus { >>>> //... >>>> public function addHandler(string $message, Handler $handler) { >>>> //... } >>>> public function getHandler(string $messageType): Handler { //... } >>>> public function dispatch($message) >>>> { >>>> $this->getHandler(get_class($message))->handle(message: >>>> $message); >>>> } >>>> } >>>> >>>> This code breaks at run time. >>>> >>>> Proposals were made for resolutions to this issue however all of them >>>> require trade offs and could potentially break existing code. >>>> >>>> My proposal to resolve these two issues is to add the ability to rename >>>> parameters with a new syntax as follows. >>>> >>>> function callBar(Foo $internalName:externalName) { >>>> $internalName->bar(); >>>> } >>>> >>>> $x = new Foo(); >>>> callBar(externalName: $x); >>>> >>>> This allows both the above problems to be resolved, by renaming the >>>> internal parameter and keeping the external signature the same. >>>> >>>> I propose that the RFC would have two voting options. >>>> >>>> The first would be to implement it as proposed above, this would allow >>>> any >>>> parameter to be called by name regardless of the intentions of the >>>> author >>>> of the method/function and is closest to the current behaviour. >>>> >>>> The second option would be to use this syntax to make named parameters >>>> in >>>> userland code explicitly opt in. As such an additional shortcut syntax >>>> would be implemented: $: to designate a named parameter. eg >>>> >>>> function callBar($:externalName) { >>>> $externalName->bar(); >>>> } >>>> >>>> $x = new Foo(); >>>> callBar(externalName: $x); >>>> >>>> If a parameter is not opted in, a compile time error is raised: >>>> >>>> function callBar($externalName) { >>>> $externalName->bar(); >>>> } >>>> >>>> $x = new Foo(); >>>> callBar(externalName: $x); // Error: cannot call parameter >>>> $externalName by >>>> name. >>>> >>>> There are pros and cons to this second approach, on the one hand it >>>> reduces >>>> the usefulness of the named parameter syntax by requiring changes to old >>>> code to enable it (although this could probably be automated fairly >>>> easily) >>>> however it does provide a neater solution to the second problem in >>>> that, to >>>> prevent the runtime errors in the second issue example, every child >>>> class >>>> would need to use the rename syntax on it's parameter to prevent errors, >>>> whereas if we went down this route, the parent class could just not opt >>>> into the named parameter syntax and the code would function as expected. >>>> >>>> Another advantage is that with the ability to rename parameters using >>>> the >>>> opt in, we gain some flexibility to tighten up the LSP rules relating to >>>> named parameter inheritance. >>>> >>>> class Foo { >>>> public function bar($:param) { //... } >>>> public function baz($internal:external) { //... } >>>> } >>>> >>>> // OK >>>> class Bar { >>>> public function bar($renamed:param) { //... } >>>> public function baz($renamed:external) { //... } >>>> } >>>> >>>> // Compile time error cannot rename named parameter $:param (renamed to >>>> $:renamedParam) >>>> class Baz { >>>> public function bar($:renamedParam) { //... } >>>> } >>>> >>>> // Compile time error cannot rename named parameter $:external (renamed >>>> to >>>> $:renamed) >>>> class Baz { >>>> public function baz($internal:renamed) { //... } >>>> } >>>> >>>> While this would be technically possible with the first option (no opt >>>> in) >>>> it would break any existing code which renames a parameter as every >>>> parameter would be subject to these rules. >>>> >>>> I don't have Wiki karma so can't post this yet; but I want to get the >>>> ball >>>> rolling on discussion as feature freeze is coming up fast and if we >>>> want to >>>> go for the second option, that must hit before the named parameter >>>> syntax >>>> is in a tagged version of PHP. >>>> >>>> Regards, >>>> Chris >>>> >>> >> Hi, >> >> While the attribute idea does solve the BC problems, I'm not seeing how >> it really solves the polymorphism issue - could you provide an example of >> how this would work? >> >> In either case; as a personal preference I'd rather stick to syntax that >> affects the parameter as part of it's definition instead of tweaking it's >> behaviour with an attribute. It just feels easier to grasp what is going on. >> > > Attributes are part of the declaration from PHP 8 on, and make them prime > implementation device for this kind of functionality, because they don't > require new keywords (same goes for ReadOnly and many other proposals that > were shot down leading up to PHP 8). > > You are right about poloymorphism, that would "only" work when every > implementation adds @@NameAlias("message"). You could devise another > attribute for this that gets inherited: @@InheritName or something (with > probably a better name needing to be found). >
Do you have an implementation example of using attributes that can be added to the RFC: https://wiki.php.net/rfc/renamed_parameters Thanks, Chris