valhalla-dev Digest, Vol 6, Issue 3
Thomas W
twhitmore.nz at gmail.com
Fri Dec 26 01:28:49 UTC 2014
Hi Brian, people,
Thanks for your thoughts! A few responses, some questions and a couple of
other points:
1) Re: List<int>.remove() disambiguation
-----------------------------------------------------------
Your solution here amounts to a deprecation mechanism with hints, which is
> an OK solution to the remove(int)/remove(Object) problem.
>
Yes, but having thought further, it would probably be clearest to
definitively rename the methods & annotate the "legacy compatibility" name
to generate a bridge method.
@LegacyName(whenUnambiguous="remove")
public T removeAt (int index);
2) Type signature of removeAll(Collection<?>), remove(Object item)
-----------------------------------------------------------
-------------------------------------
I made a start on addressing this in my previous post; in regards to the
type of remove() 'item' parameter.
At that stage I called the concept "compatible" -- which is perhaps too
strong, as the specific & exact intent is "any subtype or ancestor of T" --
essentially, any inheritance relative that could _potentially_ be
compatible.
Now my understanding -- which may or may not be exactly correct -- is that
remove() and removeAll() signatures were typed wider, to allow for clients
(with less knowledge of specific type) to remove elements from collections
created with a more-specific type.
So the possibility I proposed, was to express this as a type expression.
For reference types, it would resolve as before; for value types, absent
any notion of inheritance, it would just flatten to the value type.
<CT compatible T> public boolean removeItem (CT item);
<CT compatible T> public boolean removeAll (Collection<CT> items);
Of course, syntax is just invented here :)
3) General thoughts & new proposal on Method-Level vs Layers
--------------------------------------------------------------------------------------------
Essentially there are two ways to solve the kind of problems we're
discussing.
1) annotations & type-expression syntaxes, at the method level
2) groups of the above, as a "Layer".
I'm now going to propose a third alternative, for consideration:
3) method level, using shared type-expressions visible only within the
class.
public interface ListLike <T> {
protected type CT compatible T; // type-expression shared across
methods, but not public
public boolean removeItem (CT item);
public boolean removeAll (Collection<CT> items);
}
This third alternative achieves "sharing" similar to Layers/Peeling of type
expressions, while keeping method declarations & annotations independent.
We would gain some of the benefits of layering, but only introduce a new
member type -- which can be ignored for almost all other purposes -- rather
than a major structural level.
4) Combinatorial & Utility-class drawbacks to Layering
-------------------------------------------------------------------------------
Layers/Peeling have been proposed as an approach, initially for classes
genericized on one type-parameter. I'm not super mathematical/ or an expert
on this, but I wonder if there are some potential problems:
1) Classes parameterized on >1 parameter, are going to suffer a
combinatorial explosion of layer signatures.
- whereas the actual requirement to specialize on, is typically going to be
one (or occasionally two) of those type-parameters.
- layering<X,Y,Z> selectors when we wish to target <X> or <X,Y> will be
awkward and ugly.
I think it may be the case that method-specific type expressions, may be
easier & more efficient to correctly express our requirements for classes
genericized on >1 parameter.
2) Utility classes which genericize individual methods/ or groups of
methods, rather than being generically typed themselves.
- Layering as proposed, applies only within the class-level.
- Layering as proposed, also requires a type-parameter to genericize on.
- generics in Java are currently perfectly usable at the method level.
- should we be ruling out specialization entirely, in all perpetuity, for
utility classes/ and individual methods?
I know nobody to date has proposed/ or envisaged specializing static
methods. We've all been thinking about collections.
But should we/ do we actually need to permanently rule this out? It may be
"a scope too far" for this iteration, but I think it's an interesting
question.
5) Map<int>.get() and "sentinel values"
--------------------------------------------------------
You misunderstand. 'Sentinel' or special values have two purposes:
1) for APIs, to return "nothing present";
2) for internals, to store "nothing present".
We are both largely against the second use. But, for a large number of uses
Map.get() returning 'null' works great.
There is much code already -- Map.get() -- returning a sentinel for "not
found". My proposal is to expressly recognize that; and ensure that better
values than 0 are used.
My initial proposal was to use -1; not perfect I agree, but the bulk of C
library APIs has been happy to do without this number for a very long time
:) However, I'm happy to revise it to Integer.MIN_VALUE. 'double' could use
either MIN_VALUE or NaN. 'boolean' will have false, and that's the best we
can do.
Since efficiency is the main reason to specialize, we shouldn't require
Map.get()'s single-word return to be "blown out" to two; nor should
wrapping in Optional be required.
Unless we're going to drop Map.get() from the API, which we shouldn't, a
sentinel is proveably the only solution.
As with Objects, anybody who actually needs to distinguish
Integer.MIN_VALUE from "not present" in Map<?,int> can use the same
approaches as present:
1) check containsKey() first, then call get()
2) use getOrDefault()
3) "mask" the sentinel with another value.
Usage of the sentinel (for single-return APIs, or -- less recommended --
for internal storage) will be up to the collection author.
Given the efficiency of a single machine-word, rather than two, and the
absence of multiple-returns or out-parameters from the Java language, this
solution is sane, efficient & suitable for performant use.
6) "Syntax digressions"
-----------------------------------
My point was to propose _clean and meaningful syntax_ as part of the
mechanism -- rather than having us discuss horrible hacks like (cast)null.
I'm also suggesting that perhaps, 'null' might become invalid syntax --
having to being explicitly replaced by either of the two meaningful uses
for it, 'emptyValue' or 'sentinelValue'.
7) </Rant off>
--------------------
Well, it's good that we are introducing some FP functionality into Java. My
rant came from deep & long-held feelings and experience.
There has been, I guess, an amazing blindness in refusal to address
null-handling. Many application developers spend 10% of their lives
cleaning up other people's null-handling.
I, however, had it sorted. I had beautiful syntax. I had perfect naming. I
had it sweet.. Optional.eq() was what i called it :)
It's good that we've finally got java.util.Objects, with an
Objects.equals() method; soon, we'll no longer need to be 6-compatible and
can actually use that class.
Here are some constructive suggestions, based on what I actually did:
- Objects.text() method returning "" rather than "null", is extremely
useful.
- eqNoCase() also very useful for business applications.
Thanks for your good work on FP.. I know it's not all complete yet.
(Optional -> Stream of 0/1 is yet lacking). See:
http://stackoverflow.com/questions/22725537/using-java-8s-optional-with-streamflatmap
8) ArrayList<boolean> & custom specialization
---------------------------------------------------------------------
We shouldn't be trying to specialize this down to the 'bit' level --
machine addressing runs at the byte level only.
I'm largely against custom specializations -- there's a perfectly good
Class mechanism to let more-specific implementations of an interface be
provided. For example, you can implement a collection based on BitSet.
-------------------------
Thanks Brian & Timo for your feedback, I appreciate everyone's thoughts.
Merry Christmas everybody & enjoy the holidays!
Regards,
Thomas Whitmore
More information about the valhalla-dev
mailing list