Why are there no lifted short-circuiting operators on `bool?`?
Asked Answered
Y

3

19

Why doesn't bool? support lifted && and ||? They could have lifted the true and false operators which would have indirectly added lifted && and ||.

The operators | and & are already lifted and implement the correct Three-valued logic. But of course they are not short circuiting like || and &&.

The question is why they decided not to lift those operators when creating the specification. So "It's like this because the spec says so" is no answer to the "why?".

When lifting true and false so that null is neither true nor false:

public static bool operator true(bool? x) 
{
    return x.HasValue && x.Value
}

public static bool operator false(bool? x) 
{
  return x.HasValue && !x.Value
}

This would have resulted in && and || behaving just like their non short-circuiting counterparts. Except that false && anything and true || anything would short circuit (false and true are no compile time constants in these two examples).

This would work very similar to the DBBool example on MSDN.

I see no surprising or dangerous behavior introduced by lifting these operators. Did I miss something?

I have read another SO question on this, but found none of the answers satisfying.


Jeff Yates's answer shows a nice reason for why lifting the true/false operators isn't optimal, it doesn't explain why lifting && and || directly is bad. Since operator lifting is compiler magic that special cases Nullable<T> it doesn't need to follow the overloading rules for normal types and thus would be able to offer &&/|| without lifting true.

Yand answered 5/3, 2011 at 14:24 Comment(17)
Wouldn't this assume that NULL means false ? Not everyone would agree with that. In e.g. relational theory (and SQL) NULL AND true , NULL and NULL, FALSE OR NULL is .. NULL, which is neither true nor falseVespertine
The operator false applied to null returns false. This leads to && not short circuiting and returning null&anything instead.Yand
en.wikipedia.org/wiki/Three-valued_logicSalable
I am sure that there will be a lot of meaningless answers. only Eric Lipper can give meaningful one, let's hope that he catches the question :)Superego
Maybe I'm missing your point but, if x is null in your code above, then true(x) == false(x);. That would hardly seem to make any sense.Neron
@Vespertine for example WHERE clause in SQL treats NULL as result of conditional expression as false, example WHERE X = NULL return empty set, because it is always false, even for NULL. so this is just a projection of three-state logic to two-state logic.Superego
@Jonathan Wood take a look it's implementation here: msdn.microsoft.com/en-us/library/6292hy1k.aspx when it is 0 then true(x) == false(x)Superego
@Jonathan Wood which result would you expect for value that is neither true nor false? it will return false for both.Superego
@Jonathan I don't think that this is a problem by itself. Three values logic is a bit strange.Yand
I want to mention in C# you can write v = null | true; and it will compile. Its returns true, however v = null | false is null instead of false because you cant say it is false since null is not false. An easy way to thin of it is what CodeInChaos said to me, pretend null is (false&&true) and do the operations ;). I believe the question is, if null & boolval is legal why does null && boolval not compile. He doesnt see any harmful effectsAcyclic
@acid I didn't say null is false&&true but that it can assume both values. Probably just a misleading formulation on your part.Yand
@Superego While Eric Lippert can give the definitive answer on why they did it like this, other people can give reasons for why it is a good or bad idea. The doubt the C# team did decide this one a whim. I'm sure they had reasons why they consider it a bad idea. And these reasons can be discovered by other people too.Yand
@CodeInChaos: I see. Well, anways, it worked. It made me remember and correctly as well!Acyclic
@CodeInChaos personally I didn't even understand your question :) concept of lifted operators passed me by.Superego
@Superego The idea of lifted operators is that a nullable offers the same operators as it's underlying type, and adds sensible behavior for the case for null values. They lifted most operators including & and | but not && and ||.Yand
@CodeInChaos i guess it is because they (&& and ||) can't be overloaded, and this rule was there before nullables. They decided to play their own rules :)Superego
You can indirectly overload && and || on your own types by overloading true and false. The DBBool type in the example I posted does exactly that. And of course all lifted operators are compiler magic and not possible by simply adding operators to Nullable<T>. In particular they are only available if the underlying type has them, and they mimic the types the corresponding operators on the underlying type use(made nullable of course).Yand
A
4

What you propose would create two different usage patterns for nullable types.

Consider the following code:

bool? a = null;

// This doesn't currently compile but would with lifted true/false operators.
if (a)
{
}

// Whereas this provides a consistent use of nullable types.
if (a ?? false)
{
}

For consistency in the usage of nullable types, it makes sense to not lift the true and false operators on bool. I don't know if this is the real reason why it wasn't done, but it makes sense to me.

Acrylyl answered 5/3, 2011 at 17:26 Comment(2)
I didn't know that if uses the true operator. I thought it requires an implicit conversion to bool. But I just tested it and you're right. Still this doesn't explain why they wouldn't lift && and || directly without lifting true or false.Yand
@CodeInChaos: How could you? && and || require booleans on either side (see spec) - without lifting true or false, it can't be done.Acrylyl
P
4

Since you showed that lifting true and false is technically possible, there are only two possible answers to your question (with "they" being the people who wrote the compiler/spec):

  1. It's an error in the spec, ie. they didn't think of this. (possible, but I doubt that)
  2. They thought that lifting the short-circuiting operators is potentially error-prone. It could be the same way of reasoning as why C# is completely class based (no sole functions as in C++) or why a statement like if (myNullVar) { ... } (with myNullVar being a reference) doesn't work in C# (but it does in C/C++).

I think there's always a balance between making a programming language more powerful and making it less error-prone.

Update: Just for you interest, that's what the official documentation says:

This is not allowed because it is unclear what null means in the context of a conditional.

Penguin answered 13/6, 2011 at 6:40 Comment(4)
This answer shows very nicely why lifting op_true/op_false is a bad idea given the way conditionals are implemented(IMO using op_true in conditionals is stupid, I'd require implicit convertibility to bool) it does not show why the short circuited operators shouldn't be lifted directly.Yand
@CodesInChaos: When C# made the (IMHO) unfortunate decision to implicitly hoist all operators on nullable types using the formulation that the result should be null if either operand was null, that meant that true | null and false & null can not evaluate to true and false, respectively, as would be the case with three-value logic, but instead must evaluate to null for consistency with other hoisted operators. The normal meaning of && and || is that they should return the same value as & and | would if given the same operands, but only evaluate the right operand...Syndicate
...when its value could affect the result. While it would be possible to have && and || in such fashion as to be consistent with & and | by evaluating the right-hand side when the left-hand operand is non-null, it would be rather surprising if, given bool b=false; bool? bq = false;, the expression bq && f() evaluated the right-hand side even though b & f() would not.Syndicate
@Syndicate But the C# specification has a particular section on operators of form bool? operator |(bool? x, bool? y) and bool? operator &(bool? x, bool? y), and it clearly makes an exception for bool?, to the rule that if any operand does not HasValue, the result will be null as well. So true | null is true. See for yourself. However, the spec is quite unclear when it comes to || and && and nullables. I recently asked a question about that.Asymmetric
C
0

false && anything is the same as false. However if you expect false && anything to be true only if anything is false, then !anything is what you want.

Also, true || anything is the same as true. ...And I'm not sure how you could have it return false on any condition, as it would make no sense to have "this or that" return nothing!

... why adding additional weight to the condition when it's all clear and simple as it is?

I am not usually an adept of "because this is so", but I fail to see the advantage of adding such functionality.

Cyclotron answered 5/3, 2011 at 14:29 Comment(3)
I don't mean false being a compile-time constant in that example. But an expression that already has evaluated to false, and thus making the evaluation of the right side unnecessary.Yand
false && anything is false. no?Superego
oh! so, you are talking about cond1 && cond2 while you want cond2 be be evaluated even if cond1 is false?Cyclotron

© 2022 - 2024 — McMap. All rights reserved.