valhalla-dev Digest, Vol 7, Issue 24
Vitaly Davidovich
vitalyd at gmail.com
Thu Jan 8 00:23:22 UTC 2015
So i thought having equality externalized means you don't deal with it in
the generic class - you call a black box passing a default and target item,
and get an answer. Now yes the default for ref types is null but for
better or worse null isn't going away in java so pretending it doesn't
exist is just silly.
As for sentinels (note this is different from default) we discussed this in
the other thread in terms of how to deal with them in things like hashmap.
Sent from my phone
On Jan 7, 2015 7:14 PM, "Thomas W" <twhitmore.nz at gmail.com> wrote:
> 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