PROPOSAL: Equivalence operators (formerly titled

Derek Foster vapor1 at teleport.com
Wed May 20 21:00:14 PDT 2009


Replies inline.

-----Original Message-----
>From: Mark Thornton <mthornton at optrak.co.uk>
>Sent: May 9, 2009 4:33 AM
>To: Derek Foster <vapor1 at teleport.com>
>Cc: Project Coin <coin-dev at openjdk.java.net>
>Subject: Re: PROPOSAL: Equivalence operators (formerly titled
>
>Derek Foster wrote:
>> Hi, Mark.
>>
>> Thanks for your comments.
>>
>> The proposal already does what you suggest, although I agree it is not clear from the section you quoted (which was only intended to be a brief feature summary). If you look in the specification section, you will find that in a case like "a < b" where a is Float and b is float, that since both types are convertible to a primitive type, that this conversion will be done before the operation is attempted (possibly triggering a NullPointerException on unboxing), yielding the same results as would occur in Java now.
>>
>> In some ways, I think it would be nice to have the ># and <# operators return true if both of their operands are null, even if both operands are typed as boxed primitives. This would be more similar to how ## and !# work and would be consistent for all operators if you assume that null "is equivalent to" null. I am considering changing the proposal to allow this. (Currently this operation would provoke a NullPointerException during unboxing, according to proposal version 3 and before). < and > would not be affected by this, of course. Comments?
>>
>> Derek
>>   
>My first thought was that managing null with ># and <# was attractive 
>and nicely consistent with ## and #!, but then <, > don't just return 
>false but throw a NullPointerException.

That's fine, since by their definitions, < and > don't care about either equivalence or equality: they just care about ordering. As such, they ALWAYS need to inspect the contents of the objects, and if they can't (due to a null reference) a NullPointerException is necessary. This is different from the case where <# and ># can determine that two null references are equivalent and that therefore the "greater than or equivalent" or "less than or equivalent" condition would be true, since "X or true" is true regardless of X. Thus, two null references can be determined to be "greater than or equivalent" or "less than or equivalent" without ever needing to evaluate the "greater than" or "less than" part of those clauses -- This is basically a form of short-circuit evaluation.

Of course, if the first object was null and the second object wasn't, then a NullPointerException could still be thrown even from ># and <#. In that case, the "null is equivalent to null" short-circuit evaluation couldn't be used and the full expression needs to be evaluated, possibly resulting in an NPE if the left operand is null.

>Why can't we overload >= and <= instead in the same way as < and > (i.e. 
>only apply to Comparable types which are NOT convertable to primitives).
>Then
>##, #! handle null, based on equals()
><=, >=, <, > For primitives or primitive wrappers behave as now and 
>throw NullPointerException with null wrapper values
><=, >=, <, > For Comparable types other than primitive wrappers, based 
>on compareTo, throw NullPointerException

The problem here is that if you do that, the meaning of "=" in ">=" then becomes different from the meaning of "=" in "==". That is in large part the problem that my proposal was trying to solve by introducing a new character with a meaning of "is equivalent to" instead of "is the same object as".

Thus, if "#" means "is equivalent to" (defined as either "A==null ? B==null : A.equals(B)" or "A==null ? B==null : a.compareTo(B)==0" depending on whether it's used in an equality test or an ordering test) then the definitions of the operators fall out naturally by comparison with the existing >=, <=, ==, and != operators:

So for instance since the ># operator means "greater than or equivalent", and since greater than means

   "A.compareTo(B) >= 0" 

and equivalent means 

   "A==null ? B==null : a.compareTo(B)==0"

then if we evaluate equivalence first (for short circuiting purposes), the result must be 

   "(A==null ? B==null : a.compareTo(B)==0) || (A.compareTo(B) >= 0)"

or using a bit of algebraic simplification, we get

   "(A==null && B==null) || a.compareTo(B)>=0))".

Derek




More information about the coin-dev mailing list