tc39 / proposal-record-tuple

ECMAScript proposal for the Record and Tuple value types. | Stage 2: it will change!
https://tc39.es/proposal-record-tuple/
2.5k stars 62 forks source link

Is empty tuple / record falsy? #380

Open tiansh opened 1 year ago

tiansh commented 1 year ago

I have not find any specification about the truthy / falsy about the tulpe or records. I had tested the playground and it currently report !!#[] and !!#{} as true. However, I would expect it as falsy just like what assert(!!"" === false) do. Is this something not decided yet or could anyone point me a link to the previous discuses?

ljharb commented 1 year ago

I’d expect it to be truthy, since every container in the language is truthy when empty. Being a primitive would mean it could be falsy, of course, but I’m not sure that’d be useful.

gregmartyn commented 1 year ago

The non-empty equality tests (#{some: "thing"} === #{some: "thing"} while {some: "thing"} !== {some: "thing"}) are already one of the big differences with the other containers, so different semantics for empty wouldn't be particularly surprising. In fact, having empty be falsy would be more in line with the other primitives. (like "" as mentioned above)

acutmore commented 1 year ago

I have not find any specification about the truthy / falsy about the tulpe or records.

The specification can be found in the ToBoolean abstract operation.

https://tc39.es/proposal-record-tuple/#sec-toboolean

tiansh commented 1 year ago

I would expect is as falsy:

  1. Strings can be considered as containers of characters, and empty strings are falsy already. So I would expect other primitive containers such as tuples and records to have the same behavior, which would be less surprising to me.
  2. Some languages like Python treat empty containers as falsy. This makes sense to programmers. Testing if a container is empty is a common operation during programming. However, testing if it is a tuple vs null could be less useful. Also, since we already have the ?? operator introduced into JavaScript, when users want nullish fallback, they can use ?? instead of ||. So the falsy behavior won’t make such scenarios more complex.
  3. The boolean conversion behavior does not need to align with Object or Array. As the equality comparison has already made a very different behavior here. Programmers are trained that such types have different behaviors compared to Object.
  4. You may fell that making #{} and #[] falsy but keeping [] and {} are thuthy confusion, but same thing already happened on other types, for example, "", 0, false are falsy, but Object(""), Object(0), Object(false) are truthy already.
LongTengDao commented 11 months ago

If empty is falsy, what value does this additional difference generate?

I can't image when any code I seen try to check specially whether there is keys in an options object. And tuple's length should be easy to check.

Expanding falsy value list should be very careful and necessary, as many type guard of existing libraries may relax their vigilance against false values.

BigInt 0n can't have ownProperty, so it's reluctantly ok. I think there will be risk, unless there is sufficient reason.

At least, I think only empty tuple could be falsy, but not record.

But again, I think there will be many user pass tuple and record to lib api which only know array and object instead of them. Think about below:

export function lib_api_2 (allows) {
    if ( !allows ) { allows = [ 'defaultAllow' ]; }
}

export function lib_api_1 (options) {
    if ( !options ) { options = { defaultFeature: true }; }
}
js-choi commented 11 months ago

"" as a falsy “container” is a precedent, but [] as a truthy container is also a precedent. I think that congruency between #[] and [] is more important than #[] being congruent with "".

This proposal has already established the following design goal: to allow generic functions to generically take arrays or tuples (see “Why are Record & Tuple not based on .get()/.set() methods like Immutable.js?”). As @LongTengDao’s example demonstrates, making !!#[] be different from !![] would undermine this genericness goal.

If you want to allow generic functions that would treat arrays and tuples equivalently, and if [] is truthy, then #[] should also be truthy. In contrast, (almost?) no one is going to write generic functions that treat strings and tuples equivalently.

tiansh commented 11 months ago

If empty is falsy, what value does this additional difference generate?

I can't image when any code I seen try to check specially whether there is keys in an options object. And tuple's length should be easy to check.

Expanding falsy value list should be very careful and necessary, as many type guard of existing libraries may relax their vigilance against false values.

BigInt 0n can't have ownProperty, so it's reluctantly ok. I think there will be risk, unless there is sufficient reason.

At least, I think only empty tuple could be falsy, but not record.

But again, I think there will be many user pass tuple and record to lib api which only know array and object instead of them. Think about below:

export function lib_api_2 (allows) {
    if ( !allows ) { allows = [ 'defaultAllow' ]; }
}

export function lib_api_1 (options) {
    if ( !options ) { options = { defaultFeature: true }; }
}

For old codes that not designed to work with immutable object (aka, records), any behavior you have chosen would definitely not working. As you cannot assign allows.defaultFeature any way without getting an error if allows is immutable. And since the record is introduced as a new feature, it wont break any exists codes, but only new codes that misused the interface by passing record while it required a object.

If you try to feed an API which require number with Object(0), you may also mass up many currently exists codes.

And for newer codes, you can simply use default parameter or ?? operator to avoid any confusion.

Frimaire commented 6 months ago

Hello, I was wondering if there's been any update about this.

looks like !!#[] === true?