tc39 / proposal-partial-application

Proposal to add partial application to ECMAScript
https://tc39.es/proposal-partial-application/
BSD 3-Clause "New" or "Revised" License
1.02k stars 25 forks source link

Ambiguity of the ... operator #18

Closed caesarsol closed 5 years ago

caesarsol commented 6 years ago

I think the ... operator could lead to confusion, being it already used for array destructuring and function variadic arguments.

My proposal is to use something more like ?..., that would be consistent with the other proposal #5 on the positional placeholders.

Haroenv commented 6 years ago

What about ...?, implying you're spreading placeholders?

elycruz commented 6 years ago

@Haroenv seems to close to spread/rest op syntax (IMO); I.e., ...rest I take it back (lol) I think the solutions for this require more study as there are still other "possible features that could/should-(probably) be considered as part of partial application solution (namely currying in general).

rbuckton commented 5 years ago

The ... token in partial application would be a direct parallel to both function "rest" parameters and call "spread" arguments, as it serves both purposes:

function f(...args) { console.log(JSON.stringify(args)); }
const g = f(1, ...);
g(2, 3); // [1, 2, 3]

Since it is effectively transposed as:

const g = (() => {
  const fn = f;
  const p0 = 1;
  return (...an) => fn(p0, ...an); // <- transposition of `...`
}();

I have also considered allowing ?... to mean "spread ? into the call here":

function f(...args) { console.log(JSON.stringify(args)); }
const g = f(1, ?...);
g([2, 3]); // [1, 2, 3];

As well as allowing ...? to mean "all the rest of the args go here":

function f(...args) { console.log(JSON.stringify(args)); }
const g = f(1, ...?);
g(2, 3); // [1, [2, 3]];

In which case ...?... would be synonymous with ... (and therefore unnecessary).

nicolo-ribaudo commented 5 years ago

I think that ?... and ...? should be swapped: ...SOMETHING in function calls already is an array spread.

rbuckton commented 5 years ago

@nicolo-ribaudo It's also used for rest in function parameters, and ? is both the argument for the call and the parameter for the resulting function. Its ambiguous either way:

function f(a, b, ...rest) {}
f(a, b, ...spread) {}

const [a, b, ...rest] = ar;
ar = [a, b, ...spread];

const { a, b, ...rest } = obj;
obj = { a, b, ...spread };

Therefore its highly ambiguous in partial application:

const g1 = f(a, b, ...); // both rest and spread (not ambiguous)
const g2 = f(a, b, ...?); // is this a `...rest` or a `...spread`?

If we were to support disambiguating the two, having ... on one side or the other of ? indicates direction and cardinality:

Otherwise we'd need a different syntax to disambiguate direction (i.e. ...<?, ...>?).

In general I believe its simpler to just have ? (and possibly ...) and avoid the complexity/ambiguity of the one-to-many/many-to-one representations.

rbuckton commented 5 years ago

Closing for now as the current proposal draft as removed support for the ... operator. We may reopen this issue for further discussion at a later date, should we decide to reintroduce this support in this or a follow-on proposal.

rajington commented 5 years ago

In case this gets re-opened, how about just using parens:

macro y = transpiled y = y(a,b) calls f with
f(?) (x) => f(x) a
f(...?) (...x) => f(...x) a, b
f([...?]) (...x) => f([...x]) [a, b]
f({...?}) (...x) => f({...x}) {0: a, 1: b}
f([...(?)]) (x) => f([...x]) [...a]
f({...(?)}) (x) => f({...x}) {...a}

Parens are already commonly used to clear up syntax (e.g. () => {} vs () => ({})), and it feels very easy to remember.

It could also play well with types: foo(...(?: string)) vs foo(...?: string[]) although that example looks much better with _.

Fenzland commented 4 years ago

I think ?... is better for rest and ...? for spread. Considering ?0, ?1 ... for specific arg, ?... for the rest args are reasonable. On the other hand, ...? for spread is intuitive and far-sighted, we can extend it to ...?0, ...?1 ... in fact, we can assume ...? as shortcut for ...?0 and ... for ...?...

hax commented 4 years ago

I think having both ...? and ?... are always confusing, we'd better only use ....

f(❓)  // x => f(x)
f(1, ..., 2) // (...x) => f(1, ...x, 2)
f(1, [...], 2) // (...x) => f(1, x, 2)
f(❓1, ...❓2, ...) // (x, y, ...z) => f(x, ...y, ...z)

(I use here for any character we finally decide, personally I prefer % which follow DOS batch script 😆, so use % in follow code example)

Ambiguity only occur if use both % and ..., so we'd better ban it!

f(...%,...) // syntax error!
f(...%1,...) // much clear what we want

// actually having multiple % is also confusing, we'd better also ban it
f(%, ..., %) // what this mean?
f(%1, ..., %1) // (x, ...y) => f(x, ...y, x)    or
f(%1, ..., %2) // (x, y, ...z) => f(x, ...z, y)   or
// (x, ...y) => f(x, y.slice(0, -1), y[y.length - 1])   ???

Current proposal use f(%1, ..., %2) semantic, but if consider ..., the order is not preserved any more which may cause confusion.

PS. the last semantic could be supported in the future by introducing %^n syntax: f(%1, ..., %^1) (^n is coming from the discussion of slice notation proposal)

PPS. I use %1 instead of %0 because: