Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: ?obj operator for real '!null' check on all nullable types #545

Closed
lachbaer opened this issue May 5, 2017 · 47 comments
Closed

Comments

@lachbaer
Copy link
Contributor

lachbaer commented May 5, 2017

Proposal: ?obj operator for real '!null' check on all nullable types

Intro

The null-considering operators ?? and ?. become increasingly popular.

It would be a consistent experience to have a ?obj operator, that makes a real non-null check on nullable types, reference and value.

Problem

null checks are usually be done by comparing the object to null.

if (obj == null) { ... }
if (obj != null) { ... }

This is what probably most programmers will write.

However, because there can be overloads of operator == and operator != those checks can be violated and a real comparison to null is not achieved.

If you want to do a real null check, then for reference types you must use

object.ReferenceEquals(obj, null);

For Nullable value types it is

nullable.HasValue;

All toghether this is not a homogeneous experience for the C# user. Also it can lead to unwanted results, in case of operator overloading.

Proposal

The introduction of a not-null-comparison operator ?obj would unify the "not null check".

if (?obj) {
  /* Do many things with object */
}

if (!?obj) throw new ArgumentNullException();

The not-null-operator checks the resulting object of the right adjacent expression - usually already an object identifier - if it is not a null reference or if it HasValue for Nullables.

Misc

I choose ?obj to be a non-null check, because the other questionmark-operators could be seen as such in a verbal sense. The null-conditional operator ?. proceeds when the object is not null, the null-coalescing operator makes a positive return of its first operand if it is not null.

Because the ? stands at the very beginning of a boolean expression I guess it should be possible to parse it without unresolvable ambiguities.

The operator precedence can be like the boolean negating operator ! and other unary operators.

@svick
Copy link
Contributor

svick commented May 5, 2017

it can lead to unwanted results, in case of operator overloading

Wouldn't this happen only in cases where the overloaded operators are broken? I don't think it's worth adding special syntax just for that case.

@casperOne
Copy link

There are simple ways to drop down to get a "real" null check, and I think that the cases are not pervasive.

Special syntax here would not be widely beneficial IMO.

@alrz
Copy link
Member

alrz commented May 5, 2017

it can lead to unwanted results, in case of operator overloading

obj is null already prevents that though, but it requires parens to be negated which is the topic of another thread on !is.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 5, 2017

The idea of this proposal is not that a new expression is really needed. There are some other ways to achieve the result, as is stated in the initial post. It's rather to unify those ways and to streamline with the other questionmark operators for an overall coherent programming experience.

As a (nice?!) sideeffect it can reduce the extensive occurences of obj != null in code, as is already done by the other questionmark operators. So it would fit in line with that, too.

Also, let's just image that the questionmark operators are going to work on value types that implement a custom 'HasValue' or 'IsValid' function, or 'IValidatable' interface. Well, that's another sheet of paper, but all three operators would make much sense in that context by offering a homogeneous syntax.

@svick
Copy link
Contributor

svick commented May 5, 2017

It's rather to unify those ways and to streamline with the other questionmark operators for an overall coherent programming experience.

That sounds a lot like this xkcd. "There are too many inconsistent ways to check for null, let's add another one!"

Also, would it really improve "coherency", if the other "just a questionmark" operator, i.e. the ternary operator, behaved differently?

let's just image that the questionmark operators are going to work on value types that implement a custom 'HasValue' or 'IsValid' function, or 'IValidatable' interface. Well, that's another sheet of paper, but all three operators would make much sense in that context by offering a homogeneous syntax.

I thought the point of the proposed ? operator was that it always checks for null, no matter what crazy things the library that created a type does (i.e. overload ==, but don't handle null correctly).

If you want homogeneous syntax, you could use == null today, since you can overload == for nullable versions of such structs.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

Another pro-argument to me is its nearness to C/C++. A team member has once written that many people who come or came to C# have a C/C++ background. What always bothered me is that C# does not have this nice short syntax for conditionally evaluating null pointers and references, due to its very good type safety.

MyClass mc = MyClass::Create();
if (mc) {
  /***/
}

vs.

MyClass mc = MyClass.Create();
if (mc != null) {
  /***/
}

The simple ?obj would make it more alike to please C'ers:

MyClass mc = MyClass.Create();
if (?mc) {
  /***/
}

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

Just ran into a problem:

class  ValueHolder
{
    public int Value { get; set; }

    public static bool operator ==(ValueHolder left, ValueHolder right)
    {
        if (left == null || right == null) return false;
        return left.Value == right.Value;
    }
    public static bool operator !=(ValueHolder left, ValueHolder right)
    {
        if (left == null || right == null) return false;
        return left.Value != right.Value;
    }
}

// Following statement causes stack overflow:
if (f == null) { ... }

Well, obviously the operator is broken, should have used RefereceEquals(left, null) instead of left == null. The recursion would not occur if there was a broadly known?obj operator (unified for Nullables as well). the operator == then would look like:

    public static bool operator ==(ValueHolder left, ValueHolder right)
    {
        if (?left && ?right) return left.Value == right.Value;
        else return false;
    }

@CyrusNajmabadi
Copy link
Member

The simple ?obj would make it more alike to please C'ers:

I have no idea why that would be the case... ?expr is not any sort of C-lineage construct.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@CyrusNajmabadi I mean that if (?obj) ... is nearer to if (obj) ... than if (obj != null) ..., regarding appearance and brevity. Of course there is currently no actual ?obj operator in C.

@CyrusNajmabadi
Copy link
Member

I'm not really understanding the point of this proposal. The 'problem' as stated doesn't seem to actually be a problem.

@CyrusNajmabadi
Copy link
Member

I mean that if (?obj) ... is nearer to if (obj) ... than if (obj != null) ...

But what problem is this actually solving? We don't have people complaining about this. And, indeed, many people like that C# makes the checking explicit.

If you're going to introduce a way to do the same thing that can already be done, it needs to add substantial value to the language, or it needs to address some significant issue. Neither seems to be an issue here.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

The 'problem' as stated doesn't seem to actually be a problem.

The problem was that it caused a stack overflow. On first sight left == null seems logical, because that's how we normally do null checks, isn't it? I mean in your Roslyn code you do it most of the time. But it invokes the operator ==. The ?obj operator wouldn't do that and do a real not null check. Now you must take care whether you can do == null or whether a ReferenceEquals or for Nullables a HasValue is adequate. ?obj would unify all those. Every time you want a real null check you're gonna use this new operator and don't have to worry about possible race conditions (s.a.) or other kinds of semantic errors.

@CyrusNajmabadi
Copy link
Member

@lachbaer We already have several ways in the language/platform for people to do comparisons. Including:

x == y
x is y
x.Equals(y)
Equals(x, y)
ReferenceEquals(x, y)
EqualityComparer<T>.Default.Equals(x, y)

I struggle to think why we'd need something new. Especially something which doesn't seem to do anything that isn't already accomplished with existing functionality available. At best it just seems to muddy things and make the language more complex and confusing for someone to pick up and use effectively.

--

Finally, i would ask why we woul dgo and make a special language construct for the case of "If you want to do a real null check" (especially when "is null" exists already). I'm struggling to think when you'd need this. You can already do "is null" or "== null". And if "==" is overloaded, then it's probably appropriate for you to be calling the operator. And, in the rare case, where it isn't, the existing forms are more than sufficient.

--

In other words, we're not going to try to make the language more complicated for a use case that is already covered with existing constructs, and which users would barely ever use. We want to invest our efforts into cases that are overwrought today, but which would find value in much of a users code.

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented May 8, 2017

The problem was that it caused a stack overflow.

That's not a problem worth creating new syntax for. Just use is null if that you're concerned. The presence of ? won't help here as people will still accidently make mistakes.

And these stack overflows are so rare, and so easy to fix, it's not worthwhile to add a whole new language feature (which likely won't actually help people avoid this anyways).

@alrz
Copy link
Member

alrz commented May 8, 2017

But it invokes the operator ==

Just to mention, IDE could help with that by using a different color on user-defined operators.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@CyrusNajmabadi

Just use is null if that you're concern.

No, it's is not null . Or differently: if (!(obj is null)) ... should be if (?obj) ... by this proposal, with much less characters.

Besides, putting the negating ! way in front of the is is even more muddled than != null where the ! is directly aside the =. 😞

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@CyrusNajmabadi How about introducing a nis operator? The not is equivalence to != 🤣

@dstarkowski
Copy link

How about introducing a nis operator

How about using real words, so that people can understand them?

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@dstarkowski You saw the rofl smiley at the end? 😉
BTW, that's called irony.

@dstarkowski
Copy link

@lachbaer Yeah I did. Thought it was clear I mixed your words as a joke to sum up what I think about this proposal. But I see I need to explain.

variable is null

Real words. Even better, grammatically correct sentence.

ReferenceEquals

Real words again. Not a sentence but very obvious.

!?variable

What!?

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@dstarkowski Your confused smiley made me think that you took my comment literally and conciously.

To ride on your wave, you cannot tell from a ?? b directly what it does. When you know that ? is broadly used in (not/) 'null' contexts throughout the language I believe that one can read ?obj as well. And we do not need to discuss the using of ! for 'not' and whether it is too slim or what so ever, because then we have to dive into the ifnot discussion again.

@dstarkowski
Copy link

dstarkowski commented May 8, 2017

Comparing to ?? is poor example. There is no single line alternative. I can imagine 9 line if/else that can be transformed into single line with ??. For me writing 9 times less lines of code justifies new operators.

Same goes for ?..

This discussion is about obvious one-liner already in the language vs less obvious one-liner that needs to be added to the language.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

Without considering the double evaluation of the first operand:

c = a ?? b;
c = (a is null) ? b : a;

or with one time evaluation

var tmp = a;
c = (tmp is null) ? b : tmp;

Maybe there are some more details in the back, but to me as the stupid user those two are equal in outcome.

@dstarkowski
Copy link

dstarkowski commented May 8, 2017

@lachbaer You're right about that example. Was thinking more about exceptions, but now you can throw in ternary operator as well.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@dstarkowski

Comparing to ?? is poor example. There is no single line alternative.

So you take back that statement? 😉

@alrz
Copy link
Member

alrz commented May 8, 2017

those two are equal in outcome.

they are not. in case of a nullable value type, ?? implicitly unwraps the result.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@alrz

[selfquote:] Maybe there are some more details in the back

I could have mentioned that I meant classes. 🙄 Also I said "stupid" user, who doesn't care about the inner details.

@alrz
Copy link
Member

alrz commented May 8, 2017

This is not an "inner detail", the type of the expression clearly differs. That's just what I'm pointing out.

@dstarkowski
Copy link

@lachbaer Sure, the part about no short alternative to ?? for many cases :).

Doesn't change the fact that new operator doesn't introduce any significant value.

Also I said "stupid" user, who doesn't care about the inner details.

Not an inner detail.

bool? a = null;
var b = a ?? false;

b is not nullable.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

"Stupid user" wants a bool, whether nullable or not I don't care. 😀

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

Today this proposal has no support and won't have for the next releases.
What a pity! I really love the brevity of the C-syntax for not-null checks. But that seems to be only me 😢

@lachbaer lachbaer closed this as completed May 8, 2017
@CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi How about introducing a nis operator?

You can do that today like so:

public static class NullStuff
{
    public static bool nis(object o) => !(o == null);
}

...

using static NullStuff;

if (nis(o)) ...

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@CyrusNajmabadi Did you get by the rofl smiley that that statement was meant ironically? 😉

@CyrusNajmabadi
Copy link
Member

Nope. Didn't realize that. But my point still stands. It seems really unnecessary to need a new piece of syntax here to handle such an uncommon case. If you want a helper for this, just write one :)

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@CyrusNajmabadi

Nope. Didn't realize that.

You see, you don't understand me... 🎉 😆 😛 😉

@jnm2
Copy link
Contributor

jnm2 commented May 8, 2017

@lachbaer To be fair to everyone, text is an extremely lossy communication medium and you have to take steps to compensate for that.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@jnm2 like emoticons? 😉 I had a private communication with @CyrusNajmabadi about that, so that one's kind of an "insider".

@jnm2
Copy link
Contributor

jnm2 commented May 8, 2017

@lachbaer Emoticons sometimes help but they can be just as easily misunderstood as text to be honest.

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented May 8, 2017

Yeah, i did not pick up on that at all.

--

Anyways, my core takeaway about this bug is as follows:

  1. Language features have a super high bar to cross.
  2. Language features need to be really valuable.

If you're really just trying to make one tiny area slightly better, for a use case that only you have, then it's almost certainly never going to be something we add to the language.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

Yeah, i did not pick up on that at all.

No bother at all. 😄 Don't take me too serious, I'm very understandable regarding language issues.

for a use case that only you have

You see, that I don't really get. I absolutely cannot imagine that nobody other than me likes the C-style non-null checks and all 🎉 celebrate the explicit is null or != null or !(obj is null). Maybe when one thrives much in programming after years or decades he'll see it with different eyes - your eyes 😃 But before I am sure that there are others out there in the wild who definitely see it as I do. They're just not here.
(PS: In real life and not in front of the screen I encounter it every other day that my opinions are absolutely realistic and absolutely average. It's really hard to believe that in the particular issue of programming that shall be soooo different! 😕 )

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

@whoisj In another thread you wrote

I'm an old fashioned C guy and believe null is very useful

What's your opinion on shortening the non-null checks?

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented May 8, 2017

You see, that I don't really get. I absolutely cannot imagine that nobody other than me likes the C-style non-null checks

I'm sure people like it. But over the 15ish years we've been working on this, the feedback about this issue has been almost non-existent.

It's really hard to believe that in the particular issue of programming that shall be soooo different!

It's pretty common for people to come to a new language and want it to be like the language that they're already comfortable with. Most people though simply learn how things are different and internalize it.

When other languages provide significant value (i.e. not a reduction of a tiny number of characters), and that value would be widespread (i.e. you'd expect to see it show up in a significant percent of code of a significant percent of projects), then it's worth working on.

For the case of just being able ot restate something that is already extremely easy to state, and for the use-case of a very strange operator that i don't think you're writing properly in the first place, then this is just not proving it's worth.

--

Here's another way to help think about it. When we look at a new language feature, it usually assumed to be a huge net negative to start with. i.e. all the cost that would have to be poured into designing/implementing it. All the updates to documentation. All the training. All the confusion you now have in the ecosystem. The bifurcation. etc. etc.

So, for us to do a language feature it has to provide huge value to even get over the net negative it starts with. Huge value. If it can't demonstrate that, it's not going to happen.

Right now there is a completely reasonable way to do a null check == null or is null (if you absolutely must not involve operator). Spending effort to reduce that to "!" or "?" or "!?" is just not going to make the cut as the value being provided in no way outweighs the inherent negative value that comes with this feature.

@lachbaer
Copy link
Contributor Author

lachbaer commented May 8, 2017

It's pretty common for people to come to a new language and want it to be like the language that they're already comfortable with

I've been with C# from the very start - just not frequently. All the other times I've been doing VB(A) 6, C(++) and PHP. But I don't really like VB any more, not even the current .NET version.

Nevertheless, it could've been that many people come and say "Hooray, finally somebody who makes this obj != null be replaced by a C-like syntax and shouts it out loud". 😊

@ChuckkNorris
Copy link

ChuckkNorris commented Sep 26, 2017

This would be incredibly handy.

Consider a list of objects that could have null data that you want to compare against - if the null coalescing could also evaluate to false when checking objects then the code would be much cleaner.

Search Movies Before

// Search Movies Before Null Coalesce
toReturn = this.Movies.Where(movie => 
  (movie.Name != null && movie.Name.Contains(searchTerm))
        || (movie.Actors != null && movie.Actors.Any( actor =>
              actor.FirstName != null && actor.FirstName.ToLower().Contains(searchTerm)
              || actor.LastName != null && actor.LastName.ToLower().Contains(searchTerm)
   ))
);

Potential Search Movies After

// Search Movies
toReturn = this.Movies.Where(movie =>
    movie.Name?.Contains(searchTerm)
    || movie.Actors?.Any(actor =>
        actor.FirstName?.ToLower().Contains(searchTerm)
        || actor.LastName?.ToLower().Contains(searchTerm)
    )
);

@HaloFour
Copy link
Contributor

It would be a breaking change to modify how that operator behaves, and it would contradict the design that it always returns a nullable value. In the cases above the result of the expression would be bool? and you can compare against that, or use the ?? operator to replace null with false.

// Search Movies
toReturn = this.Movies.Where(movie =>
    (movie.Name?.Contains(searchTerm) ?? false)
    || movie.Actors?.Any(actor =>
        (actor.FirstName?.ToLower().Contains(searchTerm) ?? false)
        || (actor.LastName?.ToLower().Contains(searchTerm) ?? false)
    )
);

@NetMage
Copy link

NetMage commented Dec 7, 2017

It seems to me the ?? and ?. operator make simpler common null checks and having a base ? operator would make sensor for the uncommon null checks, or are there other categories besides field/property/method application and null coalescing that could be usefully shortened with a new operator analogous to ?? and ?. ? (After ??=, of course.)

@CyberAndrii
Copy link

And ¿obj instead of !?obj.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests