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

Q: Why not allowing null instead of false in conditionals? #438

Closed
lachbaer opened this issue Apr 14, 2017 · 21 comments
Closed

Q: Why not allowing null instead of false in conditionals? #438

lachbaer opened this issue Apr 14, 2017 · 21 comments

Comments

@lachbaer
Copy link
Contributor

Question

Conditionals in if, while, when & co must be type safe boolean expressions - for an initially good reason. (How many errors aways occur in C/C++ because of being not like that?!)

But there are so many cases where there are expressions like obj != null and obj == null.

So, what actually speaks against evaluating any non-null object or nullable to a boolean true, in case of conditionals, that being constructs, where the conditional is wrapped in parantheses (()).

Examples

(The examples show the syntax and semantics)

var obj = new Object();
if (obj) Foo();
// is equivalent to
if (obj != null) Foo();

But the following must not work

if (!obj) Foo(); // error!

because, the ! operator works on obj. That obviously doesn't work unless no implicit boolean conversion exists.

In case obj had a true operator (or bool conversion), that one will be respected, unless obj is null, what would lead to a false condition anyhow.

if (list?.Count > 100)
    ProcessManyItems(list);

would be evaluated to null if list is null. Here, also, this null could be safely evaluated to false and would still be meaningful enough.

Back to the question

Besides the whole idea of type safe boolean conditions, isn't it also safe enough to always respect null as also being false?

@jnm2
Copy link
Contributor

jnm2 commented Apr 14, 2017

It would be a breaking change if list?.Count > 100 evaluated to null because today it evaluates to false and is typed as bool, not bool?.

@svick
Copy link
Contributor

svick commented Apr 14, 2017

isn't it also safe enough to always respect null as also being false

I don't think so. At least in the case of bool? values, false is clearly distinct from null and I think conflating the two would lead to bugs.

@jnm2
Copy link
Contributor

jnm2 commented Apr 14, 2017

@svick

conflating the two would lead to bugs.

Well said.

@lachbaer
Copy link
Contributor Author

@jnm2
What's the difference? In case that bool? the expression is either true or false or null, latter two having the same outcome.
Maybe I'm having tomatoes on my eyes, but how in the world could that lead to bugs???

@jnm2
Copy link
Contributor

jnm2 commented Apr 14, 2017

@lachbaer

What's the difference?

void Foo(Bar bar, bool option) { }

int? x = null;
Foo(bar, x > 1); // Suddenly fails to compile because no overload of Foo takes (Bar, bool?).

Maybe I'm having tomatoes on my eyes, but how in the world could that lead to bugs???

var arg1 = new object();
var arg2 = new object();
if (arg1 = arg2) // Oops, this is now valid C#
{
    // ...
}

More specifically, "false" and "lack of information" are not the same thing. "No" and "N/A" should never be conflated. https://stackoverflow.com/questions/3197132/why-doesnt-null-evaluate-to-false

@jnm2
Copy link
Contributor

jnm2 commented Apr 14, 2017

@lachbaer
Copy link
Contributor Author

@jnm2
Foo(bar, x > 1); // Suddenly fails to compile is not a conditional of if (...), while (...), etc. and had no influence.

if (obj1 = obj2) // Oops, this is now valid C#- oops, the most obvious case, that I haven't thought of 😯

But in all other cases, the outcome of the conditional expression being null or false simply has the same outcome of the following statement being not executed.

@svick
Copy link
Contributor

svick commented Apr 14, 2017

Another example where this would be an issue is CheckBox.IsChecked. A checkbox can have three states:

  • checked (IsChecked == true)
  • unchecked (IsChecked == false)
  • indeterminate (IsChecked == null)

If code like if (checkbox.IsChecked) compiled, then I think programmers would often forget that the third state is also an option.

@jnm2
Copy link
Contributor

jnm2 commented Apr 14, 2017

@lachbaer

Foo(bar, x > 1); // Suddenly fails to compile is not a conditional of if (...), while (...), etc. and had no influence.

We can't have the insides of expressions be evaluated differently depending on an outer container. If the expression returns bool for a bool parameter, it must also return bool for an if condition.

@lachbaer
Copy link
Contributor Author

@jnm2

We can't have the insides of expressions be evaluated differently depending on an outer container

I think I have seen in the code that the evaluation of the conditional statements is done after the inner expression has been lowered. So, a "boolean null check" could be done then and there is no direct correlation between the boolean expression and its evaluation.

@lachbaer
Copy link
Contributor Author

@svick
But again, the outcome would be absolutely the same.

  • IsChecked == true => condition is true, execute block
  • IsChecked == false => condition is false, do not execute block
  • IsChecked "is null" => you must create an own boolean expression anyhow
    , or if (checkbox.IsChecked) is just tested: in case IsChecked isn't true it either already evaluates to false (or throws an error in case of "null", what should be avoided anyhow). The statement-block will still not be executed.

So, the question still stands. Where would this really matter ? (Except from the assignment problem)

@svick
Copy link
Contributor

svick commented Apr 14, 2017

@lachbaer The difference is, when I write if (checBox.IsChecked) today, I get a compile error, telling me to pay attention to the third case and forcing me to consciously decide what to do with it (possibly to treat it as false, but not necessarily).

With your proposal, that code would just compile, I would not think about the third case, and so there is a good chance the code I just wrote would be buggy. No pit of success for me.

@jnm2
Copy link
Contributor

jnm2 commented Apr 14, 2017

So, the question still stands. Where would this really matter ? (Except from the assignment problem)

To quote Eric Lippert:

In particular, we want to treat nullable bools as having three states: true, false and null, and not as having three states: true, false and different-kind-of-false. Treating null nullable Booleans as false leads to a number of oddities. Suppose we did, and suppose x is a nullable bool that is equal to null:

if (x)
  Foo();
if (!x)
  Bar();

Neither Foo nor Bar is executed because “not null” is of course also null. (The answer to “what is the opposite of this unknown value?” is “an unknown value”.) Does it not seem strange that x and !x are both treated as false? Similarly, if (x | !x) would also be treated as false, which also seems bizarre.

The solution to the problem of these oddities is to avoid the problem in the first place, and not make nulls behave as though they were false.

@jjvanzon
Copy link

jjvanzon commented Apr 14, 2017

I like the idea of making null checks shorter, because they are all over my code.

But (!obj) might conflict with possible future notations for non-nullability for reference types. Many would like to see '!' as a symbol for that a reference type variable cannot be null, for example:

MyClass! myVariable;

Where it means less lenience towards null, while in the proposed notation

if (!obj) Foo();

it would mean more lenience towars null, which could become confusing.

Maybe this would be better a better alternative:

if (obj?) Foo();

That notation is more like other null-conditional notations.

@lachbaer
Copy link
Contributor Author

lachbaer commented Apr 14, 2017

@svick

The difference is, when I write if (checBox.IsChecked) today, I get a compile error,

I recently figured that out 😉

In particular, we want to treat nullable bools as having three states: true, false and null,

That makes sense and it wasn't that obvious to me. Thank you all for clearing that up! 👍

@janjoostvanzon

I like the idea of making null checks shorter, because they are all over my code.

That was the main reason for this question, just first I couldn't see why null (not zero) is that different from false.

Now I have a further idea:
As if (while, when, ... resp.) needs a non-nullable bool for its evaluation, why not also having an if? (while?, when?, ...? resp.) that wants a bool? as its condition?

The questionmark-style would be absolutely intuitive and fit in the current use of it.

if? (obj) { /*...*/ }
if? (list?[index].item) { /*...*/ }
if? (checkbox.IsChecked) { /*...*/ } // Only interested in IsChecked == true
if? (objA = objB)  { /*...*/ } // put the ? there intentionally
if? (objA == objB) { /*...*/ } // Warning: condition can never be null

// equals in meaning

if (obj != null) { /*...*/ }
if (list?[index].item != null)  { /*...*/ }
if (checkbox.IsChecked.GetValueOrDefault()) { /*...*/ }
if ((objA = objB) != null) { /*...*/ }

(the condition must be either of type bool? or a type that can be compared with null)

And maybe

foreach? (var item in list)  { /*...*/ }

// equals in meaning

if (list != null) {
    foreach (var item in list)  { /*...*/ }
}

These are probably realistic use cases. What do you think?

@jjvanzon
Copy link

jjvanzon commented Apr 14, 2017

I like your enthusiasm with my reaction. I felt that my proposal would take away the awkward ambiguity between bool? being null or false, which definitely matters.

Now for the alternative notation you proposed:

if? (obj)

I feel that my proposal would work better:

if (obj?)

because you could be more specific about exactly which variable will cause the 'if' to be skipped when it is null. For instance:

if (obj? & obj2)

In your proposal both the programmer and the computer should simply 'guess' for which variable null matters? That will create ambiguity readability wise, and a performance impact computer-wise. Some people need to squeeze the max of performance out of their C# code and then the amount of guesses and checks the computer has to do matters, especially when things are called repeatedly.

My proposal would also work with other keywords, like 'while' and 'foreach'.

I do see a problem with for instance indexers and property paths and the notation I proposed:

if (list?[index])
if (obj?.obj2)

There you cannot see, if the above means 'return null if list is null' or: 'skip the if block if list is null'. The latter might be expressed as:

if (list?[index]?)
if (obj?.obj2?)

I do not know how the designers of the C# language themselves would feel about that.
I also see it extending to a possible 'must not be null' operator, where the if block is not executed when an object is null:

if (obj!)
{
    // Executed if obj is indeed filled in!
}

I also wonder how the other partipants in this conversation feel about this.

@jjvanzon
Copy link

jjvanzon commented Apr 14, 2017

I pondered over your propsal for 'return?' and 'yield return?' #437 (return only if expression is not null), where the ? being near the keywords does seem to work better.

Perhaps 'if?' could simply mean skip the if, if and only if the entire condition expression is null. I am going back and forth about this proposal of yours. Perhaps both notations could work:

if? (obj)
if (obj?)
if (obj? & obj2)

But the question mark at the end of a variable would allow you to be more specific.

@miloush
Copy link

miloush commented Apr 19, 2017

Don't forget user-defined implicit conversions to boolean, that potentially breaks many of the above mentioned assumptions.

@lachbaer
Copy link
Contributor Author

Meanwhile, after being more sensibilized for this issue, I must admit that I have only seen a few cases where obj == null could be expressed more nicely without that == null. In most cases, however, this little verbosity adds more clarity to what the auther actually meant.

Regarding the opposite construct of obj != null, I still think that there should be somehow an operator for the heavy occurences of it.

@lachbaer
Copy link
Contributor Author

The inital question has been comprehensively answered. Thank you all! 😊

@jnm2
Copy link
Contributor

jnm2 commented Apr 23, 2017

@lachbaer You're a sport. Much respect.

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

No branches or pull requests

6 participants