# Argument unpacking - Multiple unpacks and trailing arguments

- Thread: https://externals.io/message/69292
- Original archive: https://news-web.php.net/php.internals/69292
- Started by Nikita Popov on 2013-09-23T19:33:07+00:00
- Messages: 7
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #69292 — Nikita Popov — 2013-09-23T19:33:07+00:00

URL: https://externals.io/message/69292#69292

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

---

## #69294 — Lars Strojny — 2013-09-23T19:37:26+00:00

URL: https://externals.io/message/69292#69294 · In reply to: #69292

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
> 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.

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

---

## #69295 — Andrea Faulds — 2013-09-23T19:45:17+00:00

URL: https://externals.io/message/69292#69295 · In reply to: #69292

> 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/

---

## #69298 — Nikita Popov — 2013-09-23T20:19:09+00:00

URL: https://externals.io/message/69292#69298 · In reply to: #69295

> 
>
>  The main open question (or at least the focus of the discussion) seems to
> > […]
> 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

---

## #69300 — Andrea Faulds — 2013-09-23T20:34:42+00:00

URL: https://externals.io/message/69292#69300 · In reply to: #69298

> 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/

---

## #69301 — Robert Stoll — 2013-09-23T21:18:39+00:00

URL: https://externals.io/message/69292#69301 · In reply to: #69294

> -----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
> arguments
> 
> Hi Nikita,
> 
> Am 23.09.2013 um 21:33 schrieb Nikita Popov <nikita.ppv@gmail.com>:
> [...]
> > […]
> 
> 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
[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

---

## #69426 — Nikita Popov — 2013-09-30T12:56:58+00:00

URL: https://externals.io/message/69292#69426 · In reply to: #69301

> 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
