Proposal: rest operator in middle of array

Ethan Resnick ethan.resnick at gmail.com
Mon Jun 10 21:20:10 UTC 2019


> This has come up several times and, while it seems pretty intuitive to
me, not everyone seems to agree. You can check the archives for previous
discussions.

@Andy Perhaps you can provide some links? I found two
<https://esdiscuss.org/topic/an-array-destructing-specification-choice#content-69>
threads <https://esdiscuss.org/topic/early-spread-operator> — both 8 years
old — that talked about this, along with one more recent one
<https://esdiscuss.org/topic/strawman-complete-array-and-object-destructuring>
that didn't get very far. In the first two threads, commenters brought up
one case where the semantics are unclear (i.e., when there are more listed
binding elements than there are elements in the iterable), and there was
some talk about implementation complexity. But there was also some interest
from some big contributors to the spec. So I wonder if it's time to revisit
this?

> if you want to extend the function with additional args,
> then you'll have to retroactively modify all existing calls to avoid
off-by-one argcount:

@Kai I'm not sure I follow. If the new argument is required, you have to
modify all existing calls whether the new argument goes at the end or as
the second to last argument. If the new argument is optional, then adding
it as the second to last argument doesn't break existing calls at all,
assuming the function accounts for the fact that the optional arguments are
all the ones after the initial required ones, and up until (but excluding)
the last one. The syntax I'm proposing makes adding such extra arguments
easy. In other words:

```
function pad(targetLength, ...opts, data) {
  const [paddingChar = " "] = opts;
  // pad data with paddingChar to targetLength;
}
```

would, with the addition of an optional "meta" arg, become:

```
function pad(targetLength, ...opts, data) {
  const [paddingChar = " ", meta = { /* some default */ }] = opts;
  // pad data with paddingChar to targetLength;
}
```

More importantly, though, this data-last calling pattern has a long
history, and there are some cases where it's the best solution. My use case
was similar to the common one, namely, that I was building a data pipeline,
with the "settings" arguments partially applied from the left to create the
function to use in each step. (And, in my case, I was building each
function in a DSL where it would've been very inconvenient to add
arbitrary-order partial application, even if JS were to add something like
that <https://github.com/tc39/proposal-partial-application>.)

Given that functions where the last argument is significant probably aren't
going away, it'd be nice imo if the JS syntax supported this better.

On Fri, Jun 7, 2019 at 7:11 PM kai zhu <kaizhu256 at gmail.com> wrote:

> it matters when you have to debug/inherit *other* people's code (and clean
> up their mess).  i wouldn't enjoy debugging unfamiliar-code that used this
> feature (admittedly, its my subjective opinion).
>
> the maintainability argument stands -- its counter-intuitive in javascript
> that appending extra args to a function re-arranges arg-positioning and
> invalidates existing calls.
>
> debuggability is subjective i agree.
>
> p.s. - in general, i don't see what *real* painpoint rest-operator
> actually address, that couldn't be solved with `arguments`.
> variable-arg-length functions are not javascripty -- they frequently
> require extra ux-workflow transformations like Function.p.apply or
> Array.p.flatmap to the arguments being passed.
>
>
> On Fri, Jun 7, 2019 at 10:07 AM Isiah Meadows <isiahmeadows at gmail.com>
> wrote:
>
>> For your maintainability argument: adding extra arguments to those
>> functions is something I almost never do. And you'd have the same
>> exact issue with final rest parameters, just in a different position
>> (in the middle as opposed to in the end).
>>
>> For debuggability, I don't see how it'd be a major issue unless you
>> already have an excessive number of *positional* parameters. In my
>> experience, the debuggability issues arise approximately when there's
>> just too many positional parameters, and factoring out the rest
>> parameter to an array doesn't really help this situation much. (That's
>> when object destructuring comes in handy.)
>>
>> So not convinced either is any different than what it's like today.
>>
>> Also, you aren't obligated to use a feature just because it exists - I
>> hardly ever use proxies, for instance, and I rarely need maps beyond
>> what objects give me, so I don't normally use them unless I need to
>> have reference types or mixed types as keys.
>>
>> -----
>>
>> Isiah Meadows
>> contact at isiahmeadows.com
>> www.isiahmeadows.com
>>
>> On Thu, Jun 6, 2019 at 2:22 PM kai zhu <kaizhu256 at gmail.com> wrote:
>> >
>> > -1 for maintainability and debuggability
>> >
>> > 1. maintainability
>> > if you want to extend the function with additional args,
>> > then you'll have to retroactively modify all existing calls to avoid
>> off-by-one argcount:
>> >
>> > ```js
>> > // if extending function with additional args
>> > function pad(targetLength, ...opts, data) ->
>> > function pad(targetLength, ...opts, data, meta)
>> >
>> > // then must retroactively append null/undefined to all existing calls
>> > pad(1, opt1, opt2, "data") ->
>> > pad(1, opt1, opt2, "data", null)
>> > ```
>> >
>> >
>> >
>> > 2. debuggability
>> > when debugging, it takes longer for human to figure out which arg is
>> what:
>> >
>> > ```js
>> > // function pad(targetLength, ...opts, data)
>> > pad(aa, bb, cc, dd);
>> > pad(aa, bb, cc, dd, ee);
>> >
>> > // vs
>> >
>> > // function pad(targetLength, opts, data)
>> > pad(aa, [bb, cc], dd);
>> > pad(aa, [bb, cc, dd], ee);
>> > ```
>> >
>> >
>> >
>> > On Thu, Jun 6, 2019 at 11:54 AM Ethan Resnick <ethan.resnick at gmail.com>
>> wrote:
>> >>
>> >> Long-time mostly-lurker on here. I deeply appreciate all the hard work
>> that folks here put into JS.
>> >>
>> >> I've run into a couple cases now where it'd be convenient to use a
>> rest operator at the beginning or middle of an array destructuring, as in:
>> >>
>> >> ```
>> >> const [...xs, y] = someArray;
>> >> ```
>> >>
>> >> Or, similarly, in function signatures:
>> >>
>> >> ```
>> >> function(...xs, y) { }
>> >> ```
>> >>
>> >> The semantics would be simple: exhaust the iterable to create the
>> array of `xs`, like a standard rest operator would do, but then slice off
>> the last item and put it in `y`.
>> >>
>> >> For example, I was working with some variable argument functions that,
>> in FP style, always take their data last. So I had a function like this:
>> >>
>> >> ```
>> >> function match(...matchersAndData) {
>> >>   const matchers = matchersAndData.slice(0, -1);
>> >>   const data = matchersAndData[matchersAndData.length - 1];
>> >>   // do matching against data
>> >> }
>> >> ```
>> >>
>> >> Under this proposal, the above could be rewritten:
>> >>
>> >> ```
>> >> function reduce(...matchers, data) { /* ... */ }
>> >> ```
>> >>
>> >> Another example: a function `pad`, which takes a target length and a
>> string to pad, with an optional padding character argument in between:
>> >>
>> >> ```
>> >> function pad(targetLength, ...paddingCharAndOrData) {
>> >>   const [paddingChar = " "] = paddingCharAndOrData.slice(0, -1);
>> >>   const data = paddingCharAndOrData[paddingCharAndOrData.length - 1];
>> >>
>> >>   // pad data with paddingChar to targetLength;
>> >> }
>> >> ```
>> >>
>> >> With this proposal, that could be rewritten:
>> >>
>> >> ```
>> >> function pad(targetLength, ...opts, data) {
>> >>   const [paddingChar = " "] = opts;
>> >>   // pad data with paddingChar to targetLength;
>> >> }
>> >> ```
>> >>
>> >> I'm curious if this has been considered before, and what people think
>> of the idea.
>> >>
>> >> Obviously, if `...a` appeared at the beginning or middle of a list,
>> there would have to be a fixed number of items following it, so a
>> subsequent rest operator in the same list would not be allowed.
>> >>
>> >> Thanks
>> >>
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> es-discuss at mozilla.org
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> >
>> > _______________________________________________
>> > es-discuss mailing list
>> > es-discuss at mozilla.org
>> > https://mail.mozilla.org/listinfo/es-discuss
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190610/fedd8200/attachment.html>


More information about the es-discuss mailing list