Argument unpacking?

Johan Sundström oyasumi at gmail.com
Fri Nov 10 12:21:11 PST 2006


Pardon a somewhat overly long message on a stray issue, but I want to
warn against (however well meaning) syntactic sugar introduced in this
manner; it's been done before and often ends up doing a lot more harm
than good. If you have already spotted the culprit, you need not read
on.

On 11/10/06, P T Withington <ptw at pobox.com> wrote:
> My example was poor.  Replace `rest` with `[some, more, arguments]`.
>
> What I'd like to say:
>
>    foo.apply(bar, bletch, crud, [some, more, arguments])

Imagine you wanted to pass the literal argument array ["bletch",
"crud", ["some", "more", "arguments"]] (yes, the third argument being
an array) to a bar.foo which takes three arguments. It could of course
still be done, by way of either of these syntaxes:

   foo.apply(bar, "bletch", "crud", ["some", "more", "arguments"], []);
   foo.apply(bar, "bletch", "crud", [["some", "more", "arguments"]]);

...but I would guess the most common case in practice would be
unsuspecting programmers just passing their third variable blotch
(containing ["some", "more", "arguments"], run time), arriving at
bar.foo like the call

  bar.foo( "bletch", "crud", "some" );

...for all practical purposes, since this version of the function
doesn't look at any later arguments. With luck, it breaks loudly for
receiving a string where it was intended to have received an array,
but Murphy says it'll fail silently until the code has been deployed
in the wild.

> What I have to say now:
>
>    foo.apply(bar, [bletch,crud].concat([some, more, arguments]))
>
> I.e., I wish apply took any number of arguments, where the last
> argument was an array that would be 'spread' into the call:
>
>    foo.call(bar, bletch, crud, some, more, arguments)

Are you really sure that would be a good idea in practice? To cater
the case where the last argument passed to foo may sometimes actually
contain an array value, the idiomatic apply call signature would
always have to be foo.apply( bar, all, args, here, [] ) where you
*don't* use this feature, or that last "here" may end up unpacked
across a few extra arguments when it was an array, so foo's third
argument actully ends up here[0] rather than what the here variable
contained.

It looks like typical syntactic sugar for one case turning into
syntactic rat poison for all other cases to me, much like how array
literal and new Array() notation is anomalous for the case where the
constructor gets passed a single integer attribute (in at least
several implementations of javascript -- I am not sure whether it
applies to ecmascript 4 too);

new Array("high", 5);
=> ["high", 5]
new Array("hi");
=> ["hi"]
new Array(5);
=> [undefined, undefined, undefined, undefined, undefined]

Introducing a proper unpack operator would more safely avoid the
syntactic overhead you dislike, without adversely affecting other
cases, but I would rather keep the pain than defend future code
against the proposed feature. It would end up just another one of
those dangerous backwaters of the language where only the top few
percentiles would go safe and those advanced enough to have found the
apply but not its quirks, would be infested with difficult-to-find
bugs. Worse still, not always, only in a potentially uncommon case
dependent on the runtime type of the last function argument passed.

-- 
 / Johan Sundström, http://ecmanaut.blogspot.com/




More information about the Es4-discuss mailing list