Hi internals!
I'd like to finish up the argument unpacking proposal (
https://wiki.php.net/rfc/argument_unpacking).
The main open question (or at least the focus of the discussion) seems to
be whether to allow multiple unpacks and trailing arguments.
An example of multiple unpacks is the bind() function:
function bind(callable $function, ...$boundArgs) {
return function(...$args) use($function, $boundArgs) {
return $function(...$boundArgs, ...$args);
}
}
This example is detailed a bit more in the RFC:
https://wiki.php.net/rfc/argument_unpacking#partial_applicationmultiple_unpacks
An example of trailing arguments are array intersections and diffs using a
custom compare function:
array_uintersect(...$arrays, $compare);
array_udiff(...$arrays, $compare);
// also array_intersect_uassoc and all those other variations on the
topic
Some people expressed concern about allow both usages (multiple unpacks /
trailing args). I have a bit of a hard time understanding this (as there
clearly are practical applications for both), so I'd appreciate some more
opinions on the topic.
Thanks,
Nikita
Hi Nikita,
Am 23.09.2013 um 21:33 schrieb Nikita Popov nikita.ppv@gmail.com:
[...]
An example of trailing arguments are array intersections and diffs using a
custom compare function:array_uintersect(...$arrays, $compare);
array_udiff(...$arrays, $compare);
// also array_intersect_uassoc and all those other variations on the
topicSome people expressed concern about allow both usages (multiple unpacks /
trailing args). I have a bit of a hard time understanding this (as there
clearly are practical applications for both), so I'd appreciate some more
opinions on the topic.
Although it might allow users to shoot into their foot, I prefer having it instead of introducing a limitation that is not technically necessary. So +1
cu,
Lars
-----Original Message-----
From: Lars Strojny [mailto:lars@strojny.net]
Sent: Monday, September 23, 2013 9:37 PM
To: Nikita Popov
Cc: PHP internals
Subject: Re: [PHP-DEV] Argument unpacking - Multiple unpacks and trailing
argumentsHi Nikita,
Am 23.09.2013 um 21:33 schrieb Nikita Popov nikita.ppv@gmail.com:
[...]An example of trailing arguments are array intersections and diffs
using a custom compare function:array_uintersect(...$arrays, $compare);
array_udiff(...$arrays, $compare);
// also array_intersect_uassoc and all those other variations on
the topicSome people expressed concern about allow both usages (multiple
unpacks / trailing args). I have a bit of a hard time understanding
this (as there clearly are practical applications for both), so I'd
appreciate some more opinions on the topic.Although it might allow users to shoot into their foot, I prefer having it
instead of introducing a limitation that is not technically necessary. So
+1cu,
Lars
[Robert Stoll]
Personally I would allow multiple unpacking but not allow unpacking for
non-variadic parameters thus:
function foo(...$arr){} foo(...$arr, ...$arr2); //is fine
function foo(...$arr){} foo(1, 2, ...$arr, ...$arr2); //is fine
function foo($foo, $bar, ...$arr){} foo(1,2,...$arr, ...$arr2); //is fine as
well
function foo($foo, $bar, ...$arr){} foo(1, ...$arr, ...$arr2, 2);
//shouldn't be allowed IMO - ...$arr is not passed to a variadic parameter
And I would definitely not allow things like the following (but I think you
removed it already from the RFC)
function foo($bar, $baz, $buz, $boz, $booz, $booze){}
foo(..$arr, 2, ..$arr2);
I think the readability is harmed when the user is not able to tell directly
which arguments are passed to which parameters. For instance:
function foo($foo, $bar){}
function bar($foo, $bar, ...$arg){}
function doIt(Iterator $iterator){
foo(...$iterator);
bar(...$iterator);
}
Depending on the implementation of $iterator the value passed to the
parameters $foo and $bar will be different (regardless of the ...$arg in
function bar) and you will have a hard time to understand the code quickly.
As far as I understood PHP should be easy learnable, I think doing unpacking
arguments in a wrong way can hinder this and the language should restrict
it.
IMO the implementation of array_uintersect should follow the same rule as
variadics in a later version (6.x or whatever)
Cheers,
robert
Personally I would allow multiple unpacking but not allow unpacking for
non-variadic parameters thus:function foo(...$arr){} foo(...$arr, ...$arr2); //is fine
function foo(...$arr){} foo(1, 2, ...$arr, ...$arr2); //is fine
function foo($foo, $bar, ...$arr){} foo(1,2,...$arr, ...$arr2); //is fine
as
well
function foo($foo, $bar, ...$arr){} foo(1, ...$arr, ...$arr2, 2);
//shouldn't be allowed IMO - ...$arr is not passed to a variadic parameter
Introducing such a restriction doesn't make sense. A large number of uses
(you might even say "most of the uses") of call_user_func_array is in call
forwarding. In such cases the target function is usually not variadic. Also
don't forget that PHP always supported variadics via func_get_args()
and
I'd like to continue supporting that style as well. Anything that tries to
match up the unpack with a variadic arguments drop the utility of this
feature to about zero.
I think the readability is harmed when the user is not able to tell
directly
which arguments are passed to which parameters. For instance:
function foo($foo, $bar){}
function bar($foo, $bar, ...$arg){}function doIt(Iterator $iterator){
foo(...$iterator);
bar(...$iterator);
}Depending on the implementation of $iterator the value passed to the
parameters $foo and $bar will be different (regardless of the ...$arg in
function bar) and you will have a hard time to understand the code quickly.
Sorry, I don't understand what you're saying here. Unless your $iterator is
some weird "output random stuff"-iterator, I don't see why the arguments to
$foo and $bar would be different. If $iterator is a one-time iterator, the
second call will simply fail (just like any time when you use a one-time
iterator twice...)
Nikita
The main open question (or at least the focus of the discussion) seems to
be whether to allow multiple unpacks and trailing arguments.
I'm not sure I like the idea of multiple unpacks, were we to implement
named parameters (if you did f(...(['a' => 2]), ...(['a' => 3])) you'd
presumably have the $a argument overridden?), but I can't oppose it for
linear arguments alone.
I'm not sure about trailing arguments either. I'm not sure if there's
really a good use case for them.
I notice that we do not allow either in the variadics RFC, so I think it
would make sense to be consistent with that. If someone really needs to
do either of these, it would be a simple array_merge()
away, and that
might make the code clearer.
I also note that Python doesn't allow either of these. I can't find why
exactly, but I would assume there were good reasons behind it.
So I think it would be best to allow neither of those, for the sake of
consistency.
(OT: I've also just discovered that Python supports "a, b, *c = someseq"
for multiple assignment, perhaps we could see "list($a, $b, ...$c) =
$someseq" support?)
--
Andrea Faulds
http://ajf.me/
The main open question (or at least the focus of the discussion) seems to
be whether to allow multiple unpacks and trailing arguments.
I'm not sure I like the idea of multiple unpacks, were we to implement
named parameters (if you did f(...(['a' => 2]), ...(['a' => 3])) you'd
presumably have the $a argument overridden?), but I can't oppose it for
linear arguments alone.
That's a general issue that's not really related to multiple unpacks. It
could just as well happen without any unpacks at all ( f(a => 'a', a =>
'b') ) or when unpacking an iterator (which is allowed to have duplicate
keys).
I'm not sure about trailing arguments either. I'm not sure if there's
really a good use case for them.I notice that we do not allow either in the variadics RFC, so I think it
would make sense to be consistent with that.
The variadics RFC does not allow defining such functions, correct. But it's
a fact that our standard library already makes use of this pattern
(variadic arguments first and a fixed one at the end), so it seems somewhat
reasonable to support them too.
If someone really needs to do either of these, it would be a simple
array_merge()
away, and that might make the code clearer.
Why would it make the code clearer? Using the array_uintersect example:
// unpack with trailing arg
array_uintersect(...$arrays, $compare);
// array_merge
array_uintersect(...array_merge($arrays, array($compare)));
Maybe I'm alone here, but I find the first line clearer than the second.
I also note that Python doesn't allow either of these. I can't find why
exactly, but I would assume there were good reasons behind it.
Yes, Python doesn't allow this. Ruby also didn't allow this, but removed
the restriction in MRI 1.9.1. I couldn't find any reasoning for either
(i.e. why Python disallows it and why Ruby started allowing it).
Nikita
That's a general issue that's not really related to multiple unpacks. It
could just as well happen without any unpacks at all ( f(a => 'a', a =>
'b') ) or when unpacking an iterator (which is allowed to have duplicate
keys).
Fair enough. However, I'm a little concerned about the other named
parameters issue here. f(1, ...$args1, 2, ...$args2) would be quite
straightforward with numerical arguments, but with multiple unpacks
which can also unpack named parameters, I am worried things could get
quite confusing. Then again, it would be perfectly possible to write
awful code with array_merge etc. anyway, so I can't really object to it.
The variadics RFC does not allow defining such functions, correct. But
it's a fact that our standard library already makes use of this pattern
(variadic arguments first and a fixed one at the end), so it seems
somewhat reasonable to support them too.
Why would it make the code clearer? Using the array_uintersect example:
// unpack with trailing arg array_uintersect(...$arrays, $compare); // array_merge array_uintersect(...array_merge($arrays, array($compare)));
Maybe I'm alone here, but I find the first line clearer than the second.
Actually, I'm changing my mind here. I can see the utility here dealing
with existing functions. The inconsistency with the definition is still
a bit irritating, since you can call such functions easily but the
definitions would be as easy since you'd have to chop up the ...$args in
the definition, but all may be sacrificed in the name of
backwards-compatibility. :P
--
Andrea Faulds
http://ajf.me/