tc39 / proposal-nullish-coalescing

Nullish coalescing proposal x ?? y
https://tc39.github.io/proposal-nullish-coalescing/
1.23k stars 23 forks source link

What about NaN? #28

Closed aarondcohen closed 1 year ago

aarondcohen commented 6 years ago

If the point of this operator is to replace data in a likely invalid state with data in a valid state, then we should cover all javascript constants that typically represent invalid data. In that case:

let some_index = parseInt(user_input) ?? -1;
let minimum = parseInt(user_input) ?? -Infinity;
let maximum = parseInt(user_input) ?? Infinity;
ljharb commented 6 years ago

The point is to handle cases where property access would throw (which is only null and undefined); this isn’t the case for NaN.

aarondcohen commented 6 years ago

It's not really clear why this proposal is tightly linked to property access (other than they both have ? in the operator). A better guide is the ternary operator for x ? x : y.

The property access operator ?. essentially lifts the left side of the operator into the optional monad to allow for member access chaining (similar to &. in ruby or ?. in swift/c#). Presumably the end result of a chain where no value can be found is undefined.

The null-ish operator (similar to // in perl or the null coalescing operator in other languages) is about distinguishing falsey values due to an invalid state from falsey values that result from valid operations. The primary use of these operators is to make it easy to create defaults for input data. I have never known a use case where someone purposefully set a value to NaN.

claudepache commented 6 years ago

The null-ish operator (similar to // in perl or the null coalescing operator in other languages) is about distinguishing falsey values due to an invalid state from falsey values that result from valid operations.

The nullish coalescing operator is not able to distinguish falsy values according to their origin (invalid state vs. valid operation), as there is no objective way to distinguish between a ”valid” and an ”invalid” NaN, for instance.

The purpose of nullish coalescing is primarily to provide a default or a fallback value when ”no value” is provided, where the notion of ”no value” is concretised by the value null.

aarondcohen commented 6 years ago

But that doesn't match the motivating numerical example:

const animationDuration = response.settings?.animationDuration || 300; // Potentially unintended. 0 evaluates to false, result: 300
// vs
const animationDuration = response.settings?.animationDuration ?? 300;

In this case, we're determining which operator to use based on context. The assumption is the chain resolves successfully, but we want to preserve the value at the end of the chain.

animationDuration `\ \ ` ??
undefined 300 300
null 300 300
0 300 0
NaN 300 ?

It makes far more sense for NaN to resolve to 300 than to stay NaN

claudepache commented 6 years ago

There is a fundamental difference between null and NaN, though:

The null-coalescing operator should not be designed to be the numeric equivalent of the ”empty catch clause” for exceptions.

hax commented 6 years ago

@aarondcohen It seems all languages which have null coalescing operator treat NaN as non-null (I tested C#, Swift, Kotlin, perl). So I think we'd better keep the same semantic.

triskweline commented 6 years ago

I have been using the null coalescing operators for quite some time in CoffeeScript. It's just ? there, but has the same semantics as this proposal.

I also remember being surprised that ? doesn't branch on truthy/falsey values. When you refactor from if or tertiary operator to the null coalescing operator, this is something a developer might expect.

However if we did this, then we would also need pass on false, which should always considered a "given" value when setting defaults. Passing on false would lead to bugs. Hence I recommend to keep the semantics of this proposal.

phoenixeliot commented 1 year ago

All this said, I do wish there was something that could coalesce NaN values specifically, as it's awkward to compress isNaN(Number(some long expression)) ? undefined : Number(some long expression) or some long expression ? Number(some long expression) : undefined; it basically requires 2 lines of code to not repeat some long expression.

ljharb commented 1 year ago

Closing, since this proposal is at stage 4.