On Fri, 24 Jul 2020 at 14:43, Benjamin Eberlei <kont...@beberlei.de> wrote:
> > > On Fri, Jul 24, 2020 at 3:15 PM Chris Riley <t.carn...@gmail.com> wrote: > >> On Fri, 24 Jul 2020 at 14:01, Benjamin Eberlei <kont...@beberlei.de> >> wrote: >> >>> >>> >>> On Fri, Jul 24, 2020 at 1:13 PM Chris Riley <t.carn...@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