rest parameters
On 10/02/2015 11:52 AM, Michaël Rouges wrote:
Hi all,
I'm coming to you for a tiny question... excuse me if already replied...
Where the rest parameter are only declarable at the end of the arguments list, like this, please?
void function (a, ...b, c) { // b = [2, 3] }(1, 2, 3, 4);
Any real reasons?
I don't know, but I can speculate. It's not at all obvious how ...args in the middle should behave: what if you have two rest arguments? Is that forbidden, or is one greedy? What if one of the trailing parameters has a default value? Also, iiuc the spec treats "undefined" the same as "nonexistent" in most places. So what should your function do when passed (1, 2, 3, undefined)?
In short, it seems like a hairball of complexity for no real gain.
For me, the rest parameters must be unique per arguments list but, logically, usable following the function requirements...
A classical example, the node.js methods, which takes a callback as last argument, preceeded (generally) by one or more arguments, for a same method.
Michaël Rouges - Lcfvs - @Lcfvs
On Fri, Oct 2, 2015 at 12:09 PM, Steve Fink <sphink at gmail.com> wrote:
I don't know, but I can speculate. It's not at all obvious how ...args in the middle should behave: what if you have two rest arguments? Is that forbidden, or is one greedy? What if one of the trailing parameters has a default value? Also, iiuc the spec treats "undefined" the same as "nonexistent" in most places. So what should your function do when passed (1, 2, 3, undefined)?
In short, it seems like a hairball of complexity for no real gain.
Yes, this has been discussed in the past, and issues like what you brought up are the reason we've rejected it. There's lots of ambiguous situations, no clear answer for most (any?) of them, and the benefit is minimal.
I guess this is another reason why promisified versions are better:
somethingAsync(foo, bar, ...args).then(yourCallback)
On Fri, Oct 2, 2015 at 3:00 PM, Michaël Rouges <michael.rouges at gmail.com> wrote:
For the undefined case, it isn't unexistent...
void function () { console.log(arguments.length); // 3 }(1, 2, undefined);
Then, I don't see how it may be ambiguous...
Again, this has been discussed in the past. It would be useful for you to look it up in the archives and find the previous discussion.
A few examples of the ambiguity:
function f1(...a, b, c) {print arguments} f1(1) // ([], 1, undefined) or ([], undefined, 1) ?
function f2(...a, b="default") {print arguments} f2(1) // ([], 1) or ([1], "default") ?
function f3(a, b, ...c, d, e) {print arguments) f3(1,2,3) // (1, 2, [], 3, undefined) or (1, 2, [], undefined, 3) or (1, undefined, [], 2, 3) or something else?
With the rest param limited to being at the end, it's clear how to answer questions similar to these - you just assign arguments normally, and then if there are any left over, they get packed up in the rest param.
When arguments can follow the rest param, it's not clear how to do it. You can't just ignore the rest param, assign args, and then take the leftovers, because the rest param needs to take from the middle. You're clearly grabbing from "each end" and then if there's any left in the middle, you pack them into the rest param, but how to do so? That's the essence of the f1 and f3 ambiguities. And optional arguments (f2) are similarly difficult - should they "grab" more strongly than the rest param?
You can come up with answers to these questions. What you can't do is come up with answers that are obviously correct. This is why I'm not aware of any language that allows this syntax.
I'm not trying to restart debate on this, but I do know CoffeeScript allows it. It does a greedy grab, but it only allows a single spread element, and default parameters take full precedence over rest parameters in what is taken. [1]
As for optimization, that's probably the bigger issue. It's not very easily optimizable in my experience, even statically (much less at runtime). The code I usually have to write in those cases gets ugly in a hurry, and it's not a common use case. Example (it's LiveScript, which also has this, equivalent (not compiled) ES5, and equivalent ES6): [2]. Obviously, in that example, the ES5 version is a little out of hand, using array hacks in the name of speed, but the ES6 version isn't exactly pretty, and that isn't even as focused on engine speed. If you want to optimize this, good luck. The ES5 version isn't that far off of what low-level hacks would be needed to make it efficient.
Note that LiveScript does exactly the same thing as CoffeeScript in this case.
[1] coffeescript.org/#try:f %3D (a%2C b...%2C c) -> alert [a%2C b%2C c].join ' ' f 'Hello'%2C 'my'%2C 'world' [2] gist.github.com/impinball/0ea14936a1680065a3a3
Damn, forgot the reply-all button the first time. Including the original and reply here, plus my reply inline:
On Tue, 6 Oct 2015 at 19:05 Tab Atkins Jr. <jackalmage at gmail.com> wrote:
On Mon, Oct 5, 2015 at 6:33 AM, Andy Earnshaw <andyearnshaw at gmail.com> wrote:
On Sat, 3 Oct 2015 at 00:27 Tab Atkins Jr. <jackalmage at gmail.com> wrote:
A few examples of the ambiguity:
function f1(...a, b, c) {print arguments} f1(1) // ([], 1, undefined) or ([], undefined, 1) ?
function f2(...a, b="default") {print arguments} f2(1) // ([], 1) or ([1], "default") ?
function f3(a, b, ...c, d, e) {print arguments) f3(1,2,3) // (1, 2, [], 3, undefined) or (1, 2, [], undefined, 3) or (1, undefined, [], 2, 3) or something else?
I don't think these are as ambiguous as you say. For the first and third case, the first possible result you mention in both cases seems pretty natural. I don't think you could make an argument for the other cases, they don't really make any sense.
Let me rename the arguments, then, to make it more obvious:
function f1(...frontOfList, secondToLast, last) {print arguments;} f1(1) // (frontOfList=[], secondToLast=1, last=undefined) // or (frontOfList=[], secondToLast=undefined, last=1)?
It would be difficult to argue, given these argument names, that the author intended the first result. They clearly intended the second result.
Function arguments are always filled in from left to right, with the proposed exception in this case being that rest params don't swallow passed in arguments when arguments.length <= fn.length. I don't think anyone coming from a background in writing code would expect the second result, any more than they would expect:
function f1 (first, last) { print arguments; }
f1(1);
// (first=undefined, last=1)
Unless there's a precedent for this behaviour in a language I'm unaware of.
This is what I mean by:
You can come up with answers to these questions. What you can't do is come up with answers that are obviously correct. ~TJ
I don't know... the first result was "obviously correct" to me ;-).
Admittedly, "obviously correct" can be subjective when defining language behaviour.
Andy, look at my most recent post to this list. What do you think of that point?
On Wed, 7 Oct 2015 at 19:59 Isiah Meadows <isiahmeadows at gmail.com> wrote:
Andy, look at my most recent post to this list. What do you think of that point?
CoffeeScript appears to have exactly the behaviour I (and most others, presumably) would have expected. I agree that the code in your gist is tricky to follow as a result of this limitation on rest params and not easily optimised.
As far as I can tell, the main argument against is that the use cases aren't compelling enough to warrant the additional complexity in the spec. You said yourself that yours wasn't a common use case and although the callback-at-the-end pattern is pretty common, that's not so much an argument for rest in the middle as much as it is for defaults in the middle.
And defaults can have rest parameters afterwards. But yeah, I know it's
convenient in a few cases, but I find myself in this situation rarely, so
I'm not sure if it merits being in the spec. Also, the ES6 version isn't
that much longer, and if a single .pop()
becomes a bottleneck, there's
probably larger issues in the algorithm itself.
I'm coming to you for a tiny question... excuse me if already replied...
Where the rest parameter are only declarable at the end of the arguments list, like this, please?
` void function (a, ...b, c) { // b = [2, 3] }(1, 2, 3, 4);
`
Any real reasons?
Michaël Rouges - Lcfvs - @Lcfvs