proposal-record-tuple icon indicating copy to clipboard operation
proposal-record-tuple copied to clipboard

Expand support for R&T across the existing methods that accepts objects and arrays

Open nicolo-ribaudo opened this issue 4 years ago • 22 comments

I didn't add support for:

  • Reflect.*, since they already disallow primitives even if it would make sense to use them (e.g. Reflect.get("str", 0)).
  • Proxy handler (new Proxy(target, #{})), since it's meant to contain functions so a record would only work as an empty handler.
  • ~~JSON.stringify's replacer array, just to avoid conflicts with https://github.com/tc39/proposal-record-tuple/pull/257: I'll do it in a separate PR in the future~~ EDIT: done. There are also a bunch of bugs in the current JSON implementation, where we pass R/T to AOs that expects objects.

Closes #107

This PR should be reviewed commit-by-commit. Most algorithms in the misc-updates.html file are copied from the existing spec (except for InitializeTypedArrayFromTuple and UnwrapIteratorResult): focus on the <ins> and <del> tags.

nicolo-ribaudo avatar Oct 14 '21 10:10 nicolo-ribaudo

Do you think that these should be valid?

new Map([ #{ 0: "x", 1: "y", length: 2 } ]);
new Map([ #{ 0: "x", 1: "y", length: 1 } ]);
fn.apply(thisArg, #{ 0: "x", 1: "y", length: 2 });

nicolo-ribaudo avatar Oct 14 '21 11:10 nicolo-ribaudo

Do you think that these should be valid?

new Map([ #{ 0: "x", 1: "y", length: 2 } ]);
new Map([ #{ 0: "x", 1: "y", length: 1 } ]);
fn.apply(thisArg, #{ 0: "x", 1: "y", length: 2 });

AddEntriesFromIterable doesn't currently require a length so I'm not sure a check should be added now.

Since we're adding support for tuple, and these already support array like objects, why not add array like records support as well?

mhofman avatar Oct 14 '21 18:10 mhofman

Most places only accept an arraylike, or only an iterable; Array.from is one of the only ones that accepts both. new Map only accepts an iterable - being arraylike is irrelevant. Map and Set should not accept Records, since they're not iterables.

ljharb avatar Oct 14 '21 19:10 ljharb

AddEntriesFromIterable only cares about the entries of the iterable being an object with a 0 and 1 property.

d. If Type(nextItem) is not Object, then
  i. Let error be ThrowCompletion(a newly created TypeError object).
  ii. Return ? IteratorClose(iteratorRecord, error).
e. Let k be Get(nextItem, "0").
f. IfAbruptCloseIterator(k, iteratorRecord).
g. Let v be Get(nextItem, "1").
h. IfAbruptCloseIterator(v, iteratorRecord).

mhofman avatar Oct 14 '21 19:10 mhofman

@mhofman ah, i see what you mean. yes, that's true, it seems reasonable for either Tuples or Records to be the entry objects in Map/Set/Object.fromEntries.

ljharb avatar Oct 14 '21 20:10 ljharb

In case we want to do it as part of this PR. We could also expand RelationalExpression prop in val to also accept Records and Tuples. Right now non-objects throw a type error.

This could be useful in TypeScript where in is a common pattern for 'type-guards'.

acutmore avatar Jun 16 '22 10:06 acutmore

in should definitely work on primitives that are conceptual containers.

ljharb avatar Jun 16 '22 17:06 ljharb

I don't think IsConcatSpreadable should treat tuples as arrays. IsConcatSpreadable is a hook for things that are much more array-like, such as NodeLists in HTML.

michaelficarra avatar Jul 07 '22 01:07 michaelficarra

IsConcatSpreadable is a hook for things that are much more array-like

Isn't tuple array-like?

Jack-Works avatar Jul 07 '22 03:07 Jack-Works

It is 100% arraylike; i fail to see why it wouldn’t qualify.

ljharb avatar Jul 07 '22 03:07 ljharb

I don't think IsConcatSpreadable should treat tuples as arrays. IsConcatSpreadable is a hook for things that are much more array-like, such as NodeLists in HTML.

Hi @michaelficarra , would you be able to expand on this? What would your expectation be for the following:

let a =  ['a1', 'a2'];
let b = #['b1', 'b2'];

a.concat(a); // ['a1', 'a2', 'a1', 'a2'];
a.concat(b); // ?
b.concat(a); // ?
b.concat(b); // ?

acutmore avatar Jul 07 '22 12:07 acutmore

It is 100% arraylike; i fail to see why it wouldn’t qualify.

Yes array-like in that it has a numeric length and numeric indices, but not meeting the bar for concat. Notice that general array-likes like { length: 0 } and arguments objects do not get spread by concat.

What would your expectation be for the following:

let a =  ['a1', 'a2'];
let b = #['b1', 'b2'];

a.concat(a); // ['a1', 'a2', 'a1', 'a2']
a.concat(b); // ['a1', 'a2', #['b1', 'b2']]
b.concat(a); // whatever you like
b.concat(b); // #['b1', 'b2', 'b1', 'b2'];

michaelficarra avatar Jul 07 '22 14:07 michaelficarra

I agree that concat doesn’t spread all arraylikes - but Tuple is far closer to an array than most arraylikes, as a conceptual and iterable List, with list operations on its prototype.

ljharb avatar Jul 07 '22 14:07 ljharb

I agree that concat doesn’t spread all arraylikes

This is far too weak of a statement. It spreads no array-likes. It is meant to only spread arrays. For historical HTML integration reasons, we had to add the isConcatSpreadable hook, but concat spreading should never be expanded further.

michaelficarra avatar Jul 07 '22 14:07 michaelficarra

Yes, it only spreads Arrays and NodeLists, but Tuples are also built-in list-like collections. Why wouldn’t we want to expand it further? Array concat’s flattening behavior is immensely idiomatic and useful.

ljharb avatar Jul 07 '22 14:07 ljharb

Array concat’s flattening behavior is immensely idiomatic and useful.

I'd argue that concat spreading is more-often-than-not a footgun. This is why the usage pattern of a.concat([b]) came to be: fear that b may be spread.

michaelficarra avatar Jul 07 '22 15:07 michaelficarra

That’s also why a.concat(b) is so common - the hope that if a list, it will be spread, and if not, not. Because spreading syntax also spreads strings, prior to flat existing, concat was the only way to do this, and as such it’s quite common to do so.

ljharb avatar Jul 07 '22 15:07 ljharb

There are other places where we are treating Tuples more like Arrays than 'array-like'. Namely Array.prototype.flat and JSON.stringify. A general design philosophy of Tuples is to think of them like Arrays, rather than Array likes, this can be seen in their literal syntax sharing the [ ] style and Tuple.prototype being a subset of Array.prototype.

acutmore avatar Jul 08 '22 18:07 acutmore

Additionally, Tuple has concat, which uses IsConcatSpreadable. #[a].concat(#[b]) is and must be #[a, b], and thus, [a].concat([#b]) must be [a, b] for consistency (and because that's the only thing that makes sense).

ljharb avatar Jul 08 '22 19:07 ljharb

@ljharb Nothing requires Tuple's concat to use IsConcatSpreadable.

michaelficarra avatar Jul 08 '22 19:07 michaelficarra

I think user expectations do. We added a bunch of methods to Array just so Arrays and Tuples would be consistent. If arrays can spread Arrays and NodeLists, Tuples must be able to as well, but the same logic.

ljharb avatar Jul 08 '22 19:07 ljharb

If arrays can spread Arrays and NodeLists, Tuples must be able to as well, but the same logic.

Ehhh.... NodeLists contain non-primitives pretty much by definition. I am entirely OK with Tuple.prototype.concat rejecting empty NodeLists in addition to non-empty NodeLists (which it must anyway).

bakkot avatar Jul 18 '22 21:07 bakkot