Skip to content
This repository has been archived by the owner on Jan 28, 2023. It is now read-only.

Is there also a nullish AND? #61

Closed
Primajin opened this issue Jan 7, 2020 · 31 comments
Closed

Is there also a nullish AND? #61

Primajin opened this issue Jan 7, 2020 · 31 comments

Comments

@Primajin
Copy link

Primajin commented Jan 7, 2020

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
Copy link

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
Copy link
Author

Primajin commented Jan 7, 2020

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
Copy link

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

Which is exactly what != null means.

@Primajin
Copy link
Author

Primajin commented Jan 7, 2020

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

@ExE-Boss
Copy link

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
Copy link

papb commented Feb 19, 2020

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
Copy link

ExE-Boss commented Feb 19, 2020

Annex B § The [[IsHTMLDDA]] Internal Slot, tc39/ecma262#668 and tc39/ecma262#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
Copy link

papb commented Feb 19, 2020

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

@ljharb
Copy link
Member

ljharb commented Feb 19, 2020

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

@papb
Copy link

papb commented Feb 20, 2020

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

@jridgewell
Copy link
Member

jridgewell commented Mar 13, 2020

Babel could use an operator like this: babel/babel#11248 (comment)

@thw0rted
Copy link

thw0rted commented Jun 24, 2020

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
Copy link

@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
Copy link

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
Copy link
Contributor

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
Copy link

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
Copy link
Contributor

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
Copy link

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
Copy link

thw0rted commented Jun 24, 2020

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
Copy link

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 #4 (comment).

@Mouvedia
Copy link

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

@thw0rted
Copy link

thw0rted commented Jun 24, 2020

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
Copy link
Author

I second #61 (comment) - 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
Copy link
Contributor

MatthiasKunnen commented Jun 24, 2020

?? 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
Copy link

noppa commented Jun 25, 2020

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
Copy link
Author

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
Copy link

Quozzo commented Apr 8, 2021

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
Copy link
Contributor

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
Copy link

Quozzo commented Apr 8, 2021

Too bad.

Thanks for the timely reply @MatthiasKunnen

@Primajin
Copy link
Author

Primajin commented Apr 8, 2021

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
Copy link
Member

ljharb commented Jan 28, 2023

Closing, since this proposal is at stage 4.

@ljharb ljharb closed this as completed Jan 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests