I feel null isn't the real problem, the problem is the lack of semantics attached to null.
NotNull, or so called "null safety" makes it that when you know something is not optional, if a null is passed to it, you know it's a bug.
But when you do have null again, by making something nullable, the million dollar problem is back, in that you don't know why it's null?
Is it null because it's missing, false, errored, eof, leaf node, etc. You don't know.
In my opinion what we would need is union types, and simply never use null for anything, remove it completely.
Instead you could declare a function returns a Value or Missing. Or it would return a Value or False. Or it would return a Value or EndOfFile.
And similarly you could say:
User {
String|NotProvided email;
}
Where email on user can be either a String or NotProvided.
You wouldn't want the NotProvided, EndOfFile, and all that to be full on classes though, just like type aliases of some sort, similar to null in a way, but it adds semantics.
if (email == NotProvided) {
...
}
And the language compiler would error if you try to use it where it doesn't work without checking:
String email = user.email; // Error
if (user.email != NotProvided)
{
String email = user.email
} // No error
That's exactly why I love Rust.
Of course, if you don't want to write good Rust or are lazy, you can "reimplement" null with the Option enum and unwrap() it everytime, so your code panics everytime, or do the same by returning errors without thinking about if your code should move on. You still have to think about it, but at least you're not doing null checks AND Option.NONE checks, which is extremely tedious in Java, for instance.
So, for a quick script or rapid prototyping, just unwrap/panic if you don't care about the result, like in a short code contest, and for any other use, refrain from using these, match all possibilities, implement a strongly typed Error management.
basically that's how it works in Kotlin (and Dart and others) already. String? is String|null and it's a compiler error to reference a member without a prior null check.
In Kotlin (and others) it's extremely ergonomic, too. You just say foo?.bar and coalesce with ?: so
val baz = foo?.bar ?: "foo or bar is null"
In certain cases the Kotlin compiler will infer non-nullable type after an explicit null check just like in your example such that
fun bestFunction(foo : String?) {
if(foo == null) return
foo.baz <- //now legal to call without .?
}
Not really. The gp is complaining about the lack of semantics about what a missing value means. `String?` still doesn't tell me what not having a string means. `String|NotProvided` or `String|NotFound` or `String|InvalidInput` all have the same functionality as `String|null` but tells me something about what the missing value is indicating.
I mean, generally I would love (more) union types, unfortunately so few languages have them.
For Ops requirement I think it's too specific for a language level feature. In Kotlin you'd just use a sealed type in the cases were you really need to know why function can't produce a value. Works like a charm.
NotNull, or so called "null safety" makes it that when you know something is not optional, if a null is passed to it, you know it's a bug.
But when you do have null again, by making something nullable, the million dollar problem is back, in that you don't know why it's null?
Is it null because it's missing, false, errored, eof, leaf node, etc. You don't know.
In my opinion what we would need is union types, and simply never use null for anything, remove it completely.
Instead you could declare a function returns a Value or Missing. Or it would return a Value or False. Or it would return a Value or EndOfFile.
And similarly you could say:
Where email on user can be either a String or NotProvided.You wouldn't want the NotProvided, EndOfFile, and all that to be full on classes though, just like type aliases of some sort, similar to null in a way, but it adds semantics.
And the language compiler would error if you try to use it where it doesn't work without checking: