rfc:renamed_parameters

PHP RFC: Renamed Parameters

Introduction

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.

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($registrationCommand);
}
 
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. I offer a new proposal which offers some advantages.

Proposal

My proposal to resolve these two issues is to add the ability to rename parameters, while also making named parameters explicitly opt in, 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.

An additional shortcut syntax would be implemented: $: to designate a named parameter which uses the same name both internally and externally. 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 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 could use the rename syntax on it's parameter to prevent errors or 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 could be done with the existing named parameters implementation it would break any existing code which renames a parameter as every parameter would be subject to these rules not just those that had opted in to allow named parameters.

Alternative solutions

Attributes

It has been suggested that some of the issues raised in this RFC could be solved by attributes an internal attribute could be created to specify a parameter name alias and allow argument renaming. This would work similarly to option 1, however has the downside of splitting the definition of a parameter over two parts of the code.

Option 2 could also be achieved using an attribute to either enable or disable named parameters on a per function/method basis.

Opt out named parameters

The comparison to swift brings up another alternative implementation whereby a function/method author can explicitly opt out of named parameter syntax by using the special argument reference _ we could adopt something similar as a middle ground between option 1 and option 2. An example might be

function foo($:bar) { //... }
 
foo(bar: 'test'); // Error: cannot call parameter $bar by name. 

This wouldn't allow compile time checks of inheritance issues as doing so would break existing userland code.

Backward Incompatible Changes

None

Proposed PHP Version(s)

PHP 8.0

This would be a breaking change to the existing named parameters implementation if implemented in 8.1, so if accepted must be delivered for 8.0. There have been concerns that this is a big change very close to the feature freeze cut off date for 8.0 so if accepted we may need to stage the implementation, at the release managers discretion. My proposed staged approach would be to implement the explicit opt-in ($:) syntax for named parameters as part of 8.0 and to only implement the renaming portion of the RFC for 8.1

At the moment, I propose that we accept or reject the RFC as a whole and leave it to the release managers to decide the best implementation strategy, however it may be appropriate to put this to a vote as well and I invite specific feedback on this issue.

RFC Impact

To SAPIs

Unknown (probably none?)

To Existing Extensions

Unknown (probably none?)

To Opcache

The naming changes should be done at compile time, so shouldn't impact Opcache from my understanding of it.

New Constants

None

php.ini Defaults

None

Objections

Unable to support PHP 7.x and named parameters in one library release

One of the comments to come up was that having an explicit new syntax to enable named parameters means that library authors won't be able to both support both 7.x versions and also enable named parameters. This would mean there would be an additional delay before users could benefit from the new syntax. This would arguably have a larger impact on users with this feature than others such as scalar types as they would be unable to use the feature in their code when calling libraries which have not yet dropped support for PHP 7.x

While this is certainly a factor worth serious consideration, it is mitigated by time: over time all actively maintained libraries will upgrade to only supporting PHP 8.x+ and their authors can opt in the methods to use named parameters where appropriate, users will know which can support named parameter calls via the function signature and IDE support for the new syntax. PHP has been around a long time and shows no sign of slowing down. It seems short sighted to implement a feature in a way that priorities short term adoption over long term maintainability.

Very close to feature freeze

A point that has been brought up by a few people is that we are close to the feature cut off date for PHP 8.0

This is another strong point, however as things stand we are shipping a feature which breaks the object model in PHP in a way that can be avoided by a different implementation, which is going to be difficult if not impossible to fix down the line should we regret it. If we want to fix it, it needs to be done now. This is also the safer of the implementation options when compared to the current implementation; we will always be able to expand to allow all parameters to be called by name (although at the cost of keeping the redundant $: syntax) we will not be able to go back to opt-in only named params without breaking users code.

LSP breakage was documented and accepted in original RFC

It has been brought up that the problems I outlined in the introduction were brought up in the original RFC and yet it still passed an acceptance vote by an appreciable margin.

While I don't claim to speak for anyone who actually voted or their reasons for voting; from my perspective the implications for LSP did not jump out very well. In fact when I read the proposal myself my main objection came from the inclusion of parameter names within the BC breaking surface of a method or function signature, it was only after taking the time to write up this RFC that the other implications of the current implementation became fully apparent to me. If those who were able to vote were fully aware an accepting of the issues I've brought up, a no vote on this proposal will confirm that we they happy to accept the drawbacks of the current implementation, if not this gives them a chance to reconsider and choose an alternative.

Unaffected PHP Functionality

List existing areas/features of PHP that will not be changed by the RFC.

This helps avoid any ambiguity, shows that you have thought deeply about the RFC's impact, and helps reduces mail list noise.

Future Scope

This section details areas where the feature might be improved in future, but that are not currently proposed in this RFC.

Proposed Voting Choices

Straight yes/no with 2/3 majority required.

Patches and Tests

No patch yet.

Implementation

N/a

References

This proposal is similar to argument labels in swift: https://docs.swift.org/swift-book/LanguageGuide/Functions.html

Change log

26/07/20: V0.2 - Dropped option 1 (rename without explicit opt in) due to concerns over feature freeze timing, this option would be better targeted at 8.1 if this RFC fails. - Added proposed staging strategy for implementation to allay concerns over feature freeze timing - Documented objections & rebuttals to RFC

rfc/renamed_parameters.txt · Last modified: 2020/07/26 13:43 by carnage