[syntax] Inverse null coalescing operator

As there already exists a nullish coalescing operator (??), this implies a use case for an inverse nullish coalescing operator, which would still be a binary operator returning the second operand if the first one is not nullish (in contrast with ??).

Sample use-case: when fetching a possibly nullish relation:

let retrievedModel: Model | null = idOrNull === null ? null : await database.fetch<Model>(idOrNull); // the way it can be achieved now
let retrievedModel: Model | null = idOrNull !? await database.fetch<Model>(idOrNull); // inverse null coalescing operator !?

An analogic use case would involve a value of undefined or a union of undefined and null, analogically to the null coealescing operator.

The example provided is a very bad pattern. idOrNull semantically means an identifier for an object in the database or not, so why would you use this in a object that is specifically typed Model | null (this typing implying the database found an object based on the ID or not)? In general, people write

async function findModel(id: string | null): Promise<Model | null> {
  if (id === null) return null;
  // Search the database with the ID
}

which abstracts the database layer from internal logic.

Aside from the example,

  1. the nullish operator was created because of the annoyance of null when handling ternary assignment.

  2. and this (from wiki for null pointers)

In 2009 Tony Hoare (C.A.R. Hoare) stated that he invented the null reference in 1965 as part of the ALGOL W language. In that 2009 reference Hoare describes his invention as a "billion-dollar mistake":

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

By the way, here is a small exercise: Can you return null in javascript without using null in the language? (No JSON/RegExp/eval allowed)

I would argue with you on the 'very bad practice' thing. Not even mentioning that your point does not apply if the function is not implemented by hand, but provided externally (and therefore limits you to handle idOrNull being null inside it), the snippet you provided is incorrect (variable names not matching => undefined variable consider), it is also buggy in a specific case (hypothetically also consider id being 0, which would be treated like a nullish value by your code; you should have used a strict comparison operator, ===).

My example might be a specific use case, but it is just an example, one of many you can think of. In this case, it might be an API handler that accepts an optional (here, either null or Number) request parameter. And of course, findModel is just a logical placeholder for an external module, so there is no point to modify it's code, as well as my whole snippet being just a logical shorthand to provide an overview of an arbitrary use case for such an operator.

I also do not quite see your point regarding the citation, as null already exists and therefore it seems completely uncorrelated with the topic.

1 Like

Fixating on incorrect code is useless, you know what the example meant. Also, I stated abstracting from the database layer. If the function is defined externally, write a wrapper. This is best practice since external implementation details are then hidden in that function.

The point is null is not something you ever want. If you did the exercise, you would realize JS doesn’t give you null anywhere intentionally (without the exceptions I mentioned) (there is a way by the way but you would have to be a computer to find the solution). Your proposal would be the first, but as mentioned, this is bad practice.

tldr, this is bad practice. (Also, reverse nullish would give you null or undefined, not just null)

(EDIT: you should see my proposal about handling nullish values in function arguments)

By the way, here is a small exercise: Can you return null in javascript without using null in the language? (No JSON/RegExp/eval allowed)

Object.getPrototypeOf(Object.prototype)

2 Likes

What I am actually missing is a static-function equivalent to the nullish-coalescing on method calls. Where

 somethingOrNull?.method()

desugars to

somethingOrNull != null ? somethingOrNull.method() : null;

we have nothing for

somethingOrNull != null ? func(somethingOrNull) : null;

One can currently use && but that requires repetition of the whole somethingOrNull expression (which might not be a simple variable) and only works if the something is not a primitive that can have a falsy value. I'd love to be able to write just

func( somethingOrNull ?);
1 Like

One for the books! (Also disgusting lol)

This certainly has its uses, but in OOP, if there is no method, then that generally means a different type of object. One should always check the object before using a method (regardless if it’s there or not). This check can be implicit of course (meaning you know it’s there) but other than that, your use case would imply dynamic declarations of methods which IMO is also very bad practice.

What I am actually missing is a static-function equivalent to the nullish-coalescing on method calls.

As the |> pipeline operator can emulate the style of calling a method on a value

value |> methodIsh // value.method()

maybe a nullish-coalescing style pipeline operator would be useful?

value ?> methodIsh // value?.method()
1 Like

With such a solution, aren't we actually approaching an implementation similar to the operator I suggested? Using it, your second snippet could be re-written as value !? methodIsh(value) or, in conjuction with the pipeline operator, as value !? value |> methodIsh and the benefit of such a usage would be having a call to methodIsh only if value is not nullish.

One difference with a pipeline style operator is that it can be chained

let maybeComments = maybeId
                      ?> lookUpUser
                      ?> getLatestPost
                      ?> getComments

With !? :

let maybeUser = maybeId !? lookUpUser(maybeId);
let maybePost = maybeUser !? getLatestPost(maybeUser);
let maybeComments = maybePost !? getComments(maybePost)

Literally what's proposed here: https://github.com/tc39/proposal-pipeline-operator/issues/159

Edit: @ you and practically everyone else in this thread.

2 Likes

@isiahmeadows excellent! Not a bad sign when the same idea pops up in two separate places. Hopefully that gets traction