Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destructuring supported? #77

Closed
3 tasks done
giltayar opened this issue Oct 1, 2019 · 22 comments · Fixed by #97
Closed
3 tasks done

Destructuring supported? #77

giltayar opened this issue Oct 1, 2019 · 22 comments · Fixed by #97

Comments

@giltayar
Copy link

giltayar commented Oct 1, 2019

  • My issue is not bikeshedding. (we can bikeshed at
    a later proposal stage
    )
  • My issue is not a request to add lots of stuff to
    the semantics. (we can add things in follow-on proposals)
  • My issue is appropriate for a stage 0 proposal and not too
    in-the-weeds.

Not sure about this, but is destructuring supported? E.g. does this work:

const {x, y} = #{x: 4, y: 5}

I'm guessing it would, but it's definitely grey area, and I believe should be mentioned in the proposal.

But it gets worse:

const {x, y, ...z} = #{x: 4, y: 5, a: 6, b: 7}

I would probably want z to be #{a: 6, b: 7} and not {a: 6, b: 7}, so theoretically, we should have this syntax:

const #{x, y, ...z} = #{x: 4, y: 5, a: 6, b: 7}

Sidenote:
If the last is true, it puts a damper on the const alternative to #, because this would look like this:

const const {x, y, ...z} = #{x: 4, y: 5, a: 6, b: 7}
@ljharb
Copy link
Member

ljharb commented Oct 1, 2019

you could still do export const #{x, y, ...z} = record, no? it'd just make the values be Records.

@giltayar
Copy link
Author

giltayar commented Oct 1, 2019

@ljharb: Well, yes, I believe they should be. It's just not in the current proposal, and should be either added, or explicitly specified that it is not supported.

(not sure about that export in your example. typo, or did I miss something important?)

@ljharb
Copy link
Member

ljharb commented Oct 2, 2019

it should be the same with or without the export.

@caiolima
Copy link

caiolima commented Oct 2, 2019

In the case of using let #{...rest} = exp, what would be the behavior when exp evaluates to an Object? Also, What happens if we then have an record into Object destructuring?

I can think in 2 options here that is throwing TypeError or coercing types. However, I don’t like the idea of throwing a TypeError, because it means that we would have some inflexibility when using destructuring into formal parameters. I also don’t like the idea of having type coercion here, because they may lead to a lot of confusion.

@giltayar
Copy link
Author

giltayar commented Oct 2, 2019

@caiolima if I remember correctly, destructuring is specified as a series of steps being done to set variable values (which is why a value can dynamically use the value of a previous variable in the destructuring). In this case, I see no reason why the right side cannot be an object and the left a record, and vice versa.

@littledan
Copy link
Member

littledan commented Jan 20, 2020

I want to propose that destructuring with record and tuple syntax be a syntax error (at least in this initial proposal). We don't allow most kinds of literals on the right left hand side, and destructuring objects and arrays is generally liberal and not throw-y. I could imagine pattern matching records and tuples, but that's different.

@ljharb
Copy link
Member

ljharb commented Jan 20, 2020

The right hand side of destructuring is an expression that’s allowed to be any non-nullish object; it seems exceedingly strange to allow disallow record/tuple primitives. Do you mean the left hand side?

It makes no sense to me to use record/tuple syntax on the left hand side, since that’s object-like syntax for creating variables, not for creating an object (and thus, not for creating records/tuples).

@littledan
Copy link
Member

@ljharb Heh, yes, edited my comment to refer to the left, not right, side. Is it accurate to say that our proposals (of banning this construct) match?

@ljharb
Copy link
Member

ljharb commented Jan 20, 2020

Yes, it is - i don’t even see how it could possibly make any sense to allow it.

@rricard
Copy link
Member

rricard commented Jan 21, 2020

Sounds good, we should write down it's not possible to destructure them

@dcporter
Copy link

Destructuring is really nice though — instead of deferring the whole thing to a future proposal, could we just disallow the rest operator?

const rec = #{ a: "a", nestedRec: #{ deepA: "deepA" } };
const { a, nestedRec } = rec;
assert(a === "a"); // all good
assert(nestedRec === #{ deepA: "deepA" }); // all good

const { a, ...rest } = rec; // runtime error

When destructuring an object, you just get back whatever's inside. In this case, what's inside is (and can only be) a primitive or another record/tuple, so you'd get back those. So there's no controversy here, right? Just with the ...rest example above, so I would like to know if we can specifically disallow that.

@littledan
Copy link
Member

@dcporter I agree that we should permit the first part of your sample, but I don't see why we should prohibit the second part. Can't we just say that that's an Object? It's always possible to spread it out into a Record later if that's desired (even as this construct is a bit awkward).

@dcporter
Copy link

dcporter commented Jan 21, 2020

Hmm — splitting up a record and getting an object felt wrong. But now I'm poking around in the console and I see that I can use const {} destructuring on arrays to get an object back, so of course the left-hand syntax is what controls what sort of object you get back (see below*), so { ...rest } in this case would be an Object, you're right.

Given that, I think it does make sense to allow the # on the left-hand side, with the right-hand side being anything that has keys. I would be fine with punting this to a future proposal, but now I disagree with @ljharb that "It makes no sense to me to use record/tuple syntax on the left hand side, since that’s object-like syntax for creating variables, not for creating an object (and thus, not for creating records/tuples)" — the left-hand notation can already be used to specify what you want back from ...rest.

* Object-destructuring an array:

const arr = [0, 1, 2];
const { 1: one, ...rest } = arr;
console.log(one); // 1
console.log(rest); // { 0: 0, 2: 2 }

@ljharb
Copy link
Member

ljharb commented Jan 21, 2020

You’re right that a rest arg produces a container value; it does seem reasonable to me not necessarily that you can determine what you get on the LHS with syntax, but that perhaps a rest arg from a Record/Tuple is a Record/Tuple automatically?

@ljharb
Copy link
Member

ljharb commented Jan 21, 2020

In your example however, to contradict my last comment; note that rest isn’t any array, it’s a plain object, so having it be a plain object when the RHS is a Record also shouldn’t be surprising.

@dcporter
Copy link

dcporter commented Jan 21, 2020

@ljharb right: I’m now saying that destructuring with {} on the right left should result in an Object, and it would be very surprising if it didn’t. And this makes me think that having #{} syntax on the right left hand to result in a Record does make sense (though it may be complex / cuddly fiddly enough to punt to a future proposal).

@dcporter
Copy link

(gyaaaargh left not right!)

@littledan
Copy link
Member

Note that you can do this all with strings as well:

> const { length, ...props } = "hello"
> length
5
> props
Object(5) [ "h", "e", "l", "l", "o" ]   // How Firefox console displays it

I think the semantics of destructuring point to a clear answer by analogy here, that we just blindly create an Object rather than a Record in this kind of case.

@dcporter
Copy link

I agree.

(PS I also meant "fiddly" not "cuddly" in my previous; there's probably no less cuddly character than #)

@ljharb you gave my last both a 👍 and a 👎, should I assume that you're in favor of the first half and opposed to const #{…}? If so then cheers (and we can revisit in a future proposal 😄).

@ljharb
Copy link
Member

ljharb commented Jan 22, 2020

@dcporter precisely :-D

@hax
Copy link
Member

hax commented Apr 14, 2020

A "spread" on the lhs will create an object or array, not a record or tuple.

I guess in most cases programmers want record/tuple when destructuring record/tuple. So:

const [a, ...rest] = #[1, 2, 3]
rest === #[2, 3] // expect true, actual false

// Programmers need to convert it to record/tuple manually
const restTuple = #[...rest]

There are several options we could consider:

  1. Let spread create record/tuple when destructuring record/tuple.
const [a, ...rest] = #[1, 2, 3]
rest === #[2, 3] // true

Pros: Match programmer's expectation in most cases (I suppose)
Cons: May break old code which assume the result of destructuring are always object/array.

  1. Introduce a special syntax, for example:
const [a, #...rest] = #[1, 2, 3]

Pros: new syntax never affect old code
Cons: another new syntax

@ZaidRehman
Copy link

The spread operator on LHS has a lot of real-world good use cases like below.

const myFunc = ({ foo, ...rest}) => { 
      console.log(rest == #{bar: "Bar", count: 2})
}
myFunc(#{foo: "Foo", bar: "Bar", count: 2})

If you guys think it can break old code where destructuring is always an object then adding a new syntax like below will still be very useful for developers.

const myFunc = ({ foo, #...rest}) => { }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants