Allow default parameters before non-default ones?

php.internals

André Hänsel

4 years ago
PHP 8 has named parameters in function calls, Kotlin has named parameters in function calls. Kotlin has this handy feature that you can put parameters with defaults before normal ones. Those parameters can then only be used by naming them. See also: https://kotlinlang.org/docs/functions.html#default-arguments This is very useful because I can add an optional parameter to a function and prevent users of my function from using the parameter in a positional way. This way I don't have to make a compatibility promise to never change the position of this parameter. If I try the same thing in PHP 8.0 I get a Deprecated warning and if I try it in PHP 8.1 I get something that I don't understand: https://3v4l.org/cg4DA

Mark Randall

4 years ago
On 09/12/2021 05:22, André Hänsel wrote:
> This is very useful because I can add an optional parameter to a function > and prevent users of my function from using the parameter in a positional > way. This way I don't have to make a compatibility promise to never change > the position of this parameter.
I would much prefer we could find a way to give this proper support rather than a hack. I frequently use large numbers of named arguments and would very much like to be able to knock out the ability to use positional arguments, thus ensuring I can change the order (e.g. inserting a new argument after a related one) without a BC break.

Rowan Collins

4 years ago
On 09/12/2021 05:22, André Hänsel wrote:
> If I try the same thing in PHP 8.0 I get a Deprecated warning and if I try > it in PHP 8.1 I get something that I don't understand: > https://3v4l.org/cg4DA
There are two diagnostics showing in 8.1, the deprecation on the declaration, and a fatal error on the call. What's happened is that given your signature:
> function blah($foo = "deffoo", $bar) { ... }
PHP has decided it doesn't make sense, and re-interpreted it as this:
> function blah($foo, $bar) { ... }
Then when you try to call it:
> blah(bar: "1");
You're missing the now-mandatory parameter $foo, so you get a fatal error. I'm not sure exactly why this behaviour has changed, and can't see any mention in the Release or Migration pages in the manual. It's not a *huge* break, because named params have only existed since 8.0, but it is technically a breaking change in a minor version. The only relevant discussion I've found is this, where Nikita is I think hinting at the new behaviour: https://externals.io/message/114007#114026 Regards,
-- Rowan Tommins [IMSoP]

Christoph Becker

4 years ago
On 09.12.2021 at 13:03, Rowan Tommins wrote:
> On 09/12/2021 05:22, André Hänsel wrote: > >> If I try the same thing in PHP 8.0 I get a Deprecated warning and if I >> try >> it in PHP 8.1 I get something that I don't understand: >> https://3v4l.org/cg4DA > > There are two diagnostics showing in 8.1, the deprecation on the > declaration, and a fatal error on the call. > > What's happened is that given your signature: > >> function blah($foo = "deffoo", $bar) { ... } > > PHP has decided it doesn't make sense, and re-interpreted it as this: > >> function blah($foo, $bar) { ... } > > Then when you try to call it: > >> blah(bar: "1"); > > You're missing the now-mandatory parameter $foo, so you get a fatal error. > > > I'm not sure exactly why this behaviour has changed, and can't see any > mention in the Release or Migration pages in the manual. It's not a > *huge* break, because named params have only existed since 8.0, but it > is technically a breaking change in a minor version. > > The only relevant discussion I've found is this, where Nikita is I think > hinting at the new behaviour: https://externals.io/message/114007#114026
The new behavior has been introduced[1] quite a while after this mail had been sent. I agree with the idea outlined in the commit message. I suggest to document that in the migration guide, and in the manual proper. [1] <https://github.com/php/php-src/commit/afc4d67c8b4e02a985a4cd27b8e79b343eb3c0ad>
-- Christoph M. Becker

Rowan Collins

4 years ago
On 09/12/2021 12:45, Christoph M. Becker wrote:
> The new behavior has been introduced[1] quite a while after this mail > had been sent. I agree with the idea outlined in the commit message. > > I suggest to document that in the migration guide, and in the manual proper.
Good spot. I guess it does make sense to treat this as an accident in 8.0, rather than a feature that was added and then immediately removed. I've raised a PR to add this to the Migration Guide, and update the general description of optional parameters: https://github.com/php/doc-en/pull/1191 For some reason, the CI build failed on that PR, but on unrelated files, so I'm not sure if it's my fault or not. Regards,
-- Rowan Tommins [IMSoP]

Rowan Collins

4 years ago
On 09/12/2021 21:07, Rowan Tommins wrote:
> For some reason, the CI build failed on that PR, but on unrelated > files, so I'm not sure if it's my fault or not.
Ah no, it was me. Build passing now.
-- Rowan Tommins [IMSoP]

Кирилл Несмеянов

4 years ago
  >Четверг, 9 декабря 2021, 8:22 +03:00 от André Hänsel <andre@webkr.de>: >  >PHP 8 has named parameters in function calls, Kotlin has named parameters in >function calls. > >Kotlin has this handy feature that you can put parameters with defaults >before normal ones. Those parameters can then only be used by naming them. >See also: https://kotlinlang.org/docs/functions.html#default-arguments > >This is very useful because I can add an optional parameter to a function >and prevent users of my function from using the parameter in a positional >way. This way I don't have to make a compatibility promise to never change >the position of this parameter. > >If I try the same thing in PHP 8.0 I get a Deprecated warning and if I try >it in PHP 8.1 I get something that I don't understand: >https://3v4l.org/cg4DA > >-- >PHP Internals - PHP Runtime Development Mailing List >To unsubscribe, visit: https://www.php.net/unsub.php I'll add that leading optional parameters are needed to implement currying and partial application. ``` function foo(int $opt = 42, int $req) {} $foo = curry(foo(...), 23); // $foo = fn($opt = 42, $req = 23); ``` While this is not a popular practice in PHP, this deprecation notification «breaks» all code that uses functional pradigm/concepts.   -- Kirill Nesmeyanov  

Rowan Collins

4 years ago
On 13/12/2021 23:12, Kirill Nesmeyanov wrote:
> I'll add that leading optional parameters are needed to implement currying and partial application. > > ``` > function foo(int $opt = 42, int $req) {} > > $foo = curry(foo(...), 23); > > // $foo = fn($opt = 42, $req = 23); > ```
I'm confused what the optional parameter is doing here - is it just to avoid telling the curry function which argument you're fixing? And wouldn't both currying and partial application result in a single-argument closure there, not just making both parameters optional? I would expect it to look something like this: ``` function foo(int $a, int $b) {} $foo = partial(foo(...), 23, 0); // $foo = fn($b) => foo(23, $b); $foo = partial(foo(...), 23, 1); // $foo = fn($a) => foo($a, 23); $bar = curry(foo(...)); // $bar = fn($a) => fn($b) => foo($a, $b); ```
> While this is not a popular practice in PHP, this deprecation notification «breaks» all code that uses functional pradigm/concepts.
That's clearly an exaggeration; it's clearly possible to write functional-style code without making use of this particular trick. If it is a *common* trick, then that's worth considering, but it would be good to see some evidence of that. Regards,
-- Rowan Tommins [IMSoP]

Кирилл Несмеянов

4 years ago

Larry Garfield

4 years ago
On Mon, Dec 13, 2021, at 5:12 PM, Kirill Nesmeyanov wrote:
> I'll add that leading optional parameters are needed to implement > currying and partial application. > > ``` > function foo(int $opt = 42, int $req) {} > > $foo = curry(foo(...), 23); > > // $foo = fn($opt = 42, $req = 23); > ``` > > While this is not a popular practice in PHP, this deprecation > notification «breaks» all code that uses functional pradigm/concepts. >  > -- > Kirill Nesmeyanov
This is simply untrue, and I have not seen any code that does currying in that fashion. If anything, trailing optional arguments are what make currying difficult as it's unclear when to "stop" and invoke the underlying function. (The previous PFA RFC had that solved.) I do not like the original proposal here either, though. If we decide we want to allow an argument to require that it be called named, that should have an explicit keyword to indicate such. It should not be an implicit trick of otherwise confusing argument ordering. --Larry Garfield