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.

Community
  • 1
  • 1
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • 3
    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 false – nos Mar 05 '11 at 14:29
  • The operator `false` applied to `null` returns false. This leads to `&&` not short circuiting and returning `null&anything` instead. – CodesInChaos Mar 05 '11 at 14:33
  • http://en.wikipedia.org/wiki/Three-valued_logic – Jan Mar 05 '11 at 14:34
  • 1
    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 :) – Andrey Mar 05 '11 at 14:35
  • 4
    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. – Jonathan Wood Mar 05 '11 at 14:35
  • @nos 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. – Andrey Mar 05 '11 at 14:37
  • @Jonathan Wood take a look it's implementation here: http://msdn.microsoft.com/en-us/library/6292hy1k.aspx when it is 0 then `true(x) == false(x)` – Andrey Mar 05 '11 at 14:39
  • 1
    @Jonathan Wood which result would you expect for value that is neither `true` nor `false`? it will return `false` for both. – Andrey Mar 05 '11 at 14:41
  • @Jonathan I don't think that this is a problem by itself. Three values logic is a bit strange. – CodesInChaos Mar 05 '11 at 14:44
  • 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 effects –  Mar 05 '11 at 15:07
  • 1
    @acid I didn't say null is `false&&true` but that it can assume both values. Probably just a misleading formulation on your part. – CodesInChaos Mar 05 '11 at 15:10
  • 1
    @Andrey 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. – CodesInChaos Mar 05 '11 at 15:29
  • @CodeInChaos: I see. Well, anways, it worked. It made me remember and correctly as well! –  Mar 05 '11 at 15:38
  • @CodeInChaos personally I didn't even understand your question :) concept of lifted operators passed me by. – Andrey Mar 05 '11 at 15:48
  • @Andrey 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 `||`. – CodesInChaos Mar 05 '11 at 15:51
  • @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 :) – Andrey Mar 05 '11 at 15:53
  • 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`. 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). – CodesInChaos Mar 05 '11 at 15:57

3 Answers3

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.

Jeff Yates
  • 61,417
  • 20
  • 137
  • 189
  • 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`. – CodesInChaos Mar 05 '11 at 18:38
  • 1
    @CodeInChaos: How could you? && and || require booleans on either side (see spec) - without lifting `true` or `false`, it can't be done. – Jeff Yates Mar 07 '11 at 02:47
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.

Sebastian Krysmanski
  • 8,114
  • 10
  • 49
  • 91
  • 2
    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. – CodesInChaos Sep 07 '11 at 12:46
  • 1
    @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... – supercat Jun 02 '14 at 19:35
  • 1
    ...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. – supercat Jun 02 '14 at 19:43
  • @supercat 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](http://stackoverflow.com/questions/27508991/) about that. – Jeppe Stig Nielsen Dec 17 '14 at 10:58
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.

FreeAsInBeer
  • 12,937
  • 5
  • 50
  • 82
Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
  • 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. – CodesInChaos Mar 05 '11 at 14:31
  • false && anything is false. no? – Andrey Mar 05 '11 at 14:33
  • 2
    oh! so, you are talking about `cond1 && cond2` while you want `cond2` be be evaluated even if `cond1` is false? – Yanick Rochon Mar 05 '11 at 14:35