valhalla-dev Digest, Vol 7, Issue 24

Thomas W twhitmore.nz at gmail.com
Thu Jan 8 00:14:30 UTC 2015


Ok, good points. To clarify:

For ref-types:
    x eq y  -->   (x == y || (x != null && x.equals(y)))            //
'null eq null' should be true.
For primitive types:
    x eq y  -->   x == y
For value-types:
    x eq y  -->   x.equals(y)

> Getting back to the null aspect though, here's my take ...

I'm laughing.. but my point is absolutely, that we should get *away* from
it. Nulls do not belong at this abstracted level...
Neither does Duck.woof().

> we already have a "universal" equals.

We don't have a universal equals operator, that's the problem.  We have an
equals() method on Object, which requires additional null-checking to use
on nullable reftypes;  and also imposes an "asymmetrical syntax" vis a vis
it's arguments.

*There is no universal "equals" operator at the logical level.*
Simple as that.

> What you really want to say is something like this:
>
>    <any T> void foo(T t) {
>        if (T.default.equals(t))
>            System.out.println("got default/null");
>        else
>            System.out.println("got non-default/null");
>    }

No, actually I don't want to say anything like the above.    I want to
write clear code at a logical level, which applies at the level of the
domain "any-typed values" without ugly hacks.
Basically in Java, I want to express simple business requirements, and
slightly-more-interesting framework code, in a clear & straightforward way.

Let's try a better example:

    <any T>
    public boolean removeItem (T item) {
        for (int i = 0; i < size; i++) {
            if (elements[i] eq item) {
                fastRemove(i);    // found;  remove it.
                return true;
            }
        }
        return false;    // not found.
    }

The example you provided was an example of checking for a "sentinel" value
-- something I've been (separately from the EQ proposal) trying to clearly
recognize.
However, I expect such check is always the responsibility of the client
code -- Collections or framework should only answer sentinel, never be
checking for it. Therefore it is somewhat less likely to be genericized &
any-typed.

If we did wish to write such a check, however:

    <any T>
    public void processSomething (String key) {
        T result = map.get( key);
        if (result eq T.sentinel) {
            System.out.println("key presumed not present");
            return;
        }
        //.. continue processing
    }

> Now, the obvious problem here is that this NPEs on reference types since
T.default yields a null and you can't reverse the comparison because t may
be null.  So, what you'd like here is similar to what .NET does.  Introduce
an
> IEqualityComparer<T>-like JDK interface, and provide a way to obtain a
"default" comparer for a given type.  In .NET, there's a generic class
called EqualityComparer, which has a static property called Default (e.g.
> EqualityComparer<T>.Default).  That internally (in static constructor)
figures out, based on inspecting T's capabilities and type, which is the
default equality comparer and then hands out that sole instance on each
Default access.
> Ultimately, what you want here is instead of doing T.default.equals(t),
you want some other class to define an "<any T> boolean equals(T x, T y)"
which can internally handle one side being null (for ref types).

Treating "Equals" as a strategy is an interesting idea. Is it faster than
just specializing appropriate bytecode?
I'd still like a clean 'eq' operator to call this internally.

> Well behaved classes already should be handling null inputs in their
equals() impl, so we should find a way to reuse that instead of introducing
a new operator/keyword.

Well, I noticed we needed an "x == y" check to ensure "null eq null"
answered true.

There are quite a few wrinkles with Equals comparison.. one of the benefits
of bytecode/ monomorphic implementation for reftypes, might be that Hotspot
can optimize some of the checks away.

Thanks for your comments!


Regards,
Thomas

On Thu, Jan 8, 2015 at 12:33 PM, Vitaly Davidovich <vitalyd at gmail.com>
wrote:

> For value-types:
>>     x eq y  -->   (x == y)
>
>
> You mean for primitives? Custom value types won't work with == as there's
> no operator overloading.
>
> Getting back to the null aspect though, here's my take ...
>
> I think we should not introduce a new operator such as eq when we already
> have a "universal" equals.  What you really want to say is something like
> this:
>
> <any T> void foo(T t) {
>     if (T.default.equals(t))
>         System.out.println("got default/null");
>     else
>         System.out.println("got non-default/null");
> }
>
> Now, the obvious problem here is that this NPEs on reference types since
> T.default yields a null and you can't reverse the comparison because t may
> be null.  So, what you'd like here is similar to what .NET does.  Introduce
> an IEqualityComparer<T>-like JDK interface, and provide a way to obtain a
> "default" comparer for a given type.  In .NET, there's a generic class
> called EqualityComparer, which has a static property called Default (e.g.
> EqualityComparer<T>.Default).  That internally (in static constructor)
> figures out, based on inspecting T's capabilities and type, which is the
> default equality comparer and then hands out that sole instance on each
> Default access.  Ultimately, what you want here is instead of doing
> T.default.equals(t), you want some other class to define an "<any T>
> boolean equals(T x, T y)" which can internally handle one side being null
> (for ref types).
>
> tldr; well behaved classes already should be handling null inputs in their
> equals() impl, so we should find a way to reuse that instead of introducing
> a new operator/keyword.
>
>
> On Wed, Jan 7, 2015 at 6:07 PM, Thomas W <twhitmore.nz at gmail.com> wrote:
>
>> Hi Simon, Vitaly, people,
>>
>> Lots of people have been talking about adapting Collections for 'any'
>> type. Going forwards, I do not believe we should be writing code that
>> explicitly uses/ or checks for nulls (though we may be backwards-compatible
>> for a while) any more than we should be writing or calling a Duck.woof()
>> method.
>>
>> To answering a few questions:
>>
>> > a) What are the actual semantics?
>>
>> For ref-types:
>>     x eq y  -->   (x != null && x.equals(y))
>> For value-types:
>>     x eq y  -->   (x == y)
>>
>> > b) How is this different to the approaches already outlines earlier?
>>
>> It provides a basic logical building-block (equality check) at the
>> language level, in a way that works both for primitives/valuetypes and for
>> nullable references. It addresses a very big long-standing pain point in
>> Java application development. It avoids writing/ or encouraging
>> "Duck.woof()" style-hacks -- null checks -- into a domain that does not
>> uniformly support them.
>>
>> > Why do we need a new special operator? What's wrong with using equals
>> () and having the specializer rewrite that for primitives?
>> > What's wrong with using equals() and having the specializer rewrite
>> that for primitives? For other types
>> > (refs and custom value types), it should just delegate to the real
>> equals.
>>
>> To have a proper logical equals in one operator, which is independent of
>> implementation & null-safe for reftypes.
>>
>> What's wrong is writing null-checks & nulls, in a domain where nulls may
>> not exist.
>> We are not dealing with Dog, we are dealing with Animal and there should
>> be no 'woof()' method.
>>
>> > What's wrong with using equals() and having the specializer rewrite
>> that for primitives? For other types
>> > (refs and custom value types), it should just delegate to the real
>> equals.
>>
>> What's wrong with an operator that logically has exactly the right
>> meaning, without exposing details that don't exist in the domain?
>> It also provides a vast benefit for application programmers, as this kind
>> of requirement is ridiculously common in hundreds of millions of lines of
>> application code.
>>
>>
>> Regards,
>> Thomas Whitmore
>>
>
>



More information about the valhalla-dev mailing list