tc39 / proposal-nullish-coalescing

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

Is there also a nullish AND? #61

Closed Primajin closed 1 year ago

Primajin commented 4 years ago

The nullish coalescing acts like a logical OR where it returns the left part when it's not null or undefined.

Is there also an easy way for an AND where it returns the right?

Typically by chaining AND conditions one expects "the program to run from left to right and stop + return" if a condition is not met. Normally falsey values, with nullish coalescing only NIL (null + undefined)

I would like to achieve an AND chain like so:

condition1 && condition2

But it should not stop on falsey but only on null/undefined.

Is there an easy way?

!(!condition1 ?? !condition2)

Doesn't feel right.

claudepache commented 4 years ago

You can write: (condition1 != null) && condition2, or, depending how you want to use the result: (condtion1 != null) && (condition2 != null)

In any case, duplicate of #4.

Primajin commented 4 years ago

Well but I would need to write condition1 not equal null and not equal undefined and condition2 not equal null and undefined... which is cumbersome

claudepache commented 4 years ago

Well but I would need to write condition1 not equal null and not equal undefined

Which is exactly what != null means.

Primajin commented 4 years ago

Sorry my bad you are absolutely right, I am so used to !== that I didn't spot that. Thanks!

ExE-Boss commented 4 years ago

Well but I would need to write condition1 not equal null and not equal undefined

Which is exactly what != null means.

The issue is that != null will also consider objects with the [[IsHTMLDDA]] internal slot to be undefined, which ???., !== null and !== undefined won’t.

papb commented 4 years ago

Hi @ExE-Boss that is very interesting, this is the first time I hear that x != null is not strictly equivalent to x !== null && x !== undefined, can you please provide a link or an explanation where I can know more about this / see an example?

ExE-Boss commented 4 years ago

Annex B § The [[IsHTMLDDA]] Internal Slot, https://github.com/tc39/ecma262/issues/668 and https://github.com/tc39/ecma262/pull/673 explain why this legacy behaviour is needed (TL;DR: legacy IE checks).

// This is actual code that exists on old webistes:
if (document.all) {
    // Internet Explorer code
} else {
    // NetScape Navigator code
}
papb commented 4 years ago

@ExE-Boss Hmm... Do you know why this behavior does not appear directly in the Abstract Equality Comparison spec though?

ljharb commented 4 years ago

It’s in annex b, which is legacy requirements for web browsers, which currently is not inline.

papb commented 4 years ago

Nice! Thanks everyone, sorry if I drifted the topic off a little bit.

jridgewell commented 4 years ago

Babel could use an operator like this: https://github.com/babel/babel/pull/11248#discussion_r391759571

thw0rted commented 4 years ago

I'm glad to see this isn't closed. In addition to the above concerns about edge cases in the exact meaning of foo != null, I wanted to raise the point that some linters or coding standards require the use of strict equality checks (=== / !==) and treat loose equality operators as an error. That means spelling out foo !== null && foo !== undefined && bar(foo), which would look a whole lot cleaner as foo ^^ bar(foo), or whatever double-special-character pair would be safe to use there.

claudepache commented 4 years ago

@thw0rted

I'm glad to see this isn't closed.

Note that this proposal is at stage 4, which means that there will be no further changes beyond bugfixes. Any new feature will be part of another proposal.

(To the maintainers of this repo: Is it time to do some housekeeping and close most open issues?)

I wanted to raise the point that some linters or coding standards require the use of strict equality checks

We can either adapt coding standards and linter rules so that they allow us to use an existing feature of the language, or duplicate an existing feature of the language so that we can circumvent coding standards and linter rules. Personally, I tend to prefer the first option.

That means spelling out foo !== null && foo !== undefined && bar(foo), which would look a whole lot cleaner as foo ^^ bar(foo), or whatever double-special-character pair would be safe to use there.

I’m not sure that == null is substantially more confusing than your proposed ^^ (the first time I would see ^^ in an unknown language, I would wonder if it is the logical xor operator). But in any case, if you (or your coding standard) fear that a casual reader will misunderstand == null, there is still room to take advantage of existing features of the language, instead of creating yet another notation to be learned. I’m thinking of: is_nullish(foo) && bar(foo)

thw0rted commented 4 years ago

no further changes beyond bugfixes

Welp, that's that, I guess. Makes sense, and certainly this isn't nearly as important as the ?? version. Maybe next time.

adapt coding standards and linter rules so that they allow us to use an existing feature of the language

The coding standards and linter rules are there because loose equality is a footgun, and the language feature is used wrong by accident more often than it is used right on purpose. (Reasonable people can disagree about whether this is in fact true.) We could update the standards/rules to say "except when it's loose-equals null because you meant 'null or undefined', as long as you did it intentionally and know what you're doing", or we could add an operator that succinctly and explicitly means "I am checking for null or undefined".

the first time I would see ^^ in an unknown language, I would wonder if it is the logical xor operator

Me too; I picked the first special character I could think of that I was pretty sure didn't have an existing meaning -- naming things is of course one of the Hard Problems. In #4, ?! was proposed, which fits in with ?? much more naturally but I believe would conflict with a combination of the ternary and not operators, e.g. foo ? !bar : !baz. I'm confident that smarter folks than I could come up with a valid symbol combination that makes sense. (Later in that thread, someone suggested ?& and I can't think of a valid preexisting construct with those two together, so let's go with that for now.)

there is still room to take advantage of existing features of the language

It would be trivial to write a function, but you could say the same thing about ??, which made the cut. Why should ?? get in but not ?&?

MatthiasKunnen commented 4 years ago

I’m not sure that == null is substantially more confusing than your proposed ^^

I agree. In codebases I work on we use linter rules to enforce tripe equal for anything but null/undefined comparison. Using == null is only allowed when the value can be both null or undefined. This way we have == null only in the correct places and === undefined/=== null in other places.

We also have type guards such as isNotNullOrUndefined to use in functions such as array.filter.

In my eyes, adding a special operator for a case which already has a perfectly valid and concise solution is superfluous and confusing.

thw0rted commented 4 years ago

In my eyes, adding a special operator for a case which already has a perfectly valid and concise solution is superfluous and confusing.

So, you were also against the nullish operator that did get in, ?? ?

MatthiasKunnen commented 4 years ago

No I wasn't since ?? makes the following readable:

// Before with ternary
console.log(data.nickname != null ? data.nickname : (data.name != null ? data.name : 'N/A'));

// Before with functions
console.log(getDefault(getDefault(data.nickName, data.name), 'N/A'));

// Now
console.log(data.nickname ?? data.name ?? 'N/A')
thw0rted commented 4 years ago
function getDefault() {
  for (let a of arguments) {
    if (a !== null && a !== undefined) { return a; }
  }
}

getDefault(data.nickName, data.name, "N/A");

That is, IMHO, exactly as "readable" as data.nickname ?? data.name ?? 'N/A'. But the point of having a native operator is that you don't make everybody roll their own getDefault. MDN is rife with polyfills for all kinds of syntactic sugar added in the last few rounds of ES -- think about all the helper methods for interacting with Arrays, Object.entries, etc.

All I'm saying is, if we're going to add sugar, let's at least be consistent. My point above was that ?& would provide similar value to ?? in similar sorts of situations, so if we're going to have one, it makes sense to add the other. I don't see anyone here disagreeing.

thw0rted commented 4 years ago

Oops: both of our getDefault examples are wrong because they don't exhibit short-circuiting behavior. In the case of data.name ?? lookupDefaultName(), the function call won't run if data.name is non-nullish, but getDefault(data.name, lookupDefaultName()) always runs the lookup function even if it's not used.

That leaves us with the ternary operator for comparison. I believe it's about as verbose in the "AND" case as with the "OR" case:

x = data.nickname != null ? f(data.nickname) : (data.name != null ? f(data.name) : 'N/A'));
x = (data.nickname ?& f(data.nickname)) || (data.name ?& f(data.name)) || 'N/A';

To me, the latter (hypothetical) version conveys intent better and avoids the complexity of nested ternary operators.

claudepache commented 4 years ago

It would be trivial to write a function, but you could say the same thing about ??, which made the cut. Why should ?? get in but not ?&?

My requirements for a replacement of ?? and ?& using existing language features, are:

  1. It does not require to evaluate uselessly some value (short-circuiting), and:
  2. It does not require to repeat some value (DRY).

Applying DRY, I short-circuit what I was going to write and backlink to https://github.com/tc39/proposal-nullish-coalescing/issues/4#issuecomment-321968495.

Mouvedia commented 4 years ago

Do you have a list of languages that have "nullish AND"? If so, what are the operators used?

thw0rted commented 4 years ago

I don't know of a similar feature in other languages, but then the first I heard of ?? was actually in Typescript release notes, so I'm the wrong guy to ask. (I got out of C# before it had ??, close to 15 years ago.)

I agree that at this point it's looking more and more like a shorthand for != null && which is not a great argument in favor of a new feature. If it came along anyway, I'd probably use it, but if anything this is probably going to make me take a harder look at my linter config.

Primajin commented 4 years ago

I second https://github.com/tc39/proposal-nullish-coalescing/issues/61#issuecomment-648899863 - since if we add the sugar for != null || why not also for != null &&. We also lint everything to triple equals so that could be configured in the short term but having a shorthand in the long term would definitely be nice.

MatthiasKunnen commented 4 years ago

?? is not syntactic sugar for != null || though, it is different.

Anyway, in my opinion, != null is perfectly readable and I do not see a need for an operator which does exactly the same as != null. I believe it would add more confusion than benefit.

noppa commented 4 years ago

It seems that in many of the examples for this operator, the value ends up being repeated in RHS

name = data.name != null ? capitalize(data.name) : 'N/A'
name = (data.name ?& capitalize(data.name)) ?? 'N/A'

which makes me wonder if this use case was actually better served by the suggested optional chaining support in the pipeline operator proposal (pipeline proposal, optional chaining suggestion).

name = (data.name ?> capitalize) ?? 'N/A'
Primajin commented 4 years ago

Do you have a list of languages that have "nullish AND"? If so, what are the operators used?

I guess many of the popular other languages out there are typed by default - so that the need for such an "nullish AND" didn't really arose.

When dealing with data where 0 is a valid value one can easily make the mistake in ES to form a check for the value not considering that ES interprets 0 as false. That's why ?? became so handy but it doesn't feel complete without and AND version (since it's OR)

Quozzo commented 3 years ago

Is it too late to propose an alternative syntax? Since type checking is used when using three equal signs, then three OR or/and AND could be used.

null ||| true 
//returns true 

0 ||| true
//returns 0

null &&& true
//returns false

0 &&& true
//returns true

null ?? true : false
//returns false

0 ?? true : false
//returns true
MatthiasKunnen commented 3 years ago

Is it too late to propose an alternative syntax?

Yes it is. Nullish coalescing is stage 4 and already implemented by browser vendors.

See https://tc39.es/process-document/ for details about proposal stages.

Quozzo commented 3 years ago

Too bad.

Thanks for the timely reply @MatthiasKunnen

Primajin commented 3 years ago

Not sure if you mean the same thing. The syntax for nullish coalescing is indeed fix - but for nullish AND as per this thread it's still completely open and up for discussion wether this should be a thing at all

ljharb commented 1 year ago

Closing, since this proposal is at stage 4.