Under Review
over 1 year ago

Strange behavior of logical operators

I know some of this has been discussed before, but the discussions are several years old, there's no resolution, and the docs are still very misleading.

var x = !0; // compile time error


var x = 0;

var y = !x; // y = -1, not even a warning at -l 3


var x = 4, y = 8, z = 12;

if (x) { System.println("x is true"); }

if (!x) { System.println("!x is true"); }

if (y) { System.println("y is true"); }

if (z) { System.println("z is true"); }

if (x && y) {} else { System.println("x && y is false"); }

if (x && z) { System.println("x && z is true"); }

if (!(x && z)) { System.println("!(x && z) is true"); }

x is true
!x is true
y is true
z is true
x && y is false
x && z is true
!(x && z) is true

The docs say that ! "reverses the value of a logical expression", and gives !(m && n) as an example of a logical expression. You could argue that x, when x is not boolean, is not a "logical expression", but (x && z) seems to be the very definition of one...

The docs also say "The expression x && y first evaluates x. If x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned." This is wrong in several ways. First, it should say "If x is falsey", rather than "if x is false", since 0 and null both cause the short-circuit, in addition to false. Second, when x fails the falsey test, the value returned is a function of both x and y, not simply "the value of y". It appears to be x & y when both are integers (which is why "4 && 8" is treated as false), and if x is an integer, we get runtime errors unless x & y is a valid expression.

The docs seem to be mostly correct for "x || y", but then !(x || y) has very surprising results unless both x and y are boolean; and in particular !(x || y) is not the same as !x && !y, and isn't guaranteed to be falsey just because x || y was truthy...

I accept that after this long with this behavior, there's nothing you can really do to change it without risking breaking every app out there. But the documentation could be trivially fixed, and the type-checker could give an error for non-boolean arguments to "!" which could save a lot of grief (it already errors on integer args to x && y). I think the best fix for the documentation would be to specify which types are supported, and the expected results, and to explicitly say that the behavior with other types is unspecified (including the possibility of future runtime errors).