Updated State of the Specialization
Simon Ochsenreither
simon at ochsenreither.de
Sat Dec 20 01:44:15 UTC 2014
Hi everyone, Hi Brian,
thanks for keeping us updated.
Now that some of the basic parts seem to get a bit more stable, I'd like to ask
some questions and raise some issues I'm seeing:
Regarding rules about null and any-T values¹:
---------------------------------------------
Am I correct to assume that casting a null to a type is supported inside
specialized code and results in the zero value of that type (null for
references, 0/0.0/... for primitive types and e. g. Point(0,0) for value type
Point(Int, Int))?
Otherwise, what's the recommended way to "summon" a zero value for an arbitrary
type?
Comment on statics²:
--------------------
This is absolutely the right call. .NET's behavior in that regard is a disaster
of unknown proportions.
Boxing – value boxes vs. wrapper classes:
-----------------------------------------
The draft seems to be very silent on the expected semantics of how value types
are handled outside specialized code and how wrapper classes are handled inside
specialized code.
I think this is an important point, because every kind of mistake will directly
effect thousands of users. Avoiding quirks here seems to be absolutely crucial
to me (for instance, floating point numbers are hard enough to deal with even
without letting users figure out the subtle differences between primitives,
primitives-as-value-types, primitives-as-value-boxed-value-types and classic
wrapped primitives).
Primitive equality on value types/inside specialized code:
----------------------------------------------------------
Is there any further progress or additional insight into how this
issue will be adressed, especially when boxing is involved?
Partially specialized type hierarchies:
---------------------------------------
Although it isn't spelt out in the draft it seems that it is possible to go from
specialized to erased, but not from erased to specialized:
Super<any T> ---> Sub< T> // ok
Super< T> ---> Sub<any T> // not ok
If this is correct, what's the story for types which are currently erased in
byte code, because they can't be expressed with Java Generics (HKTs come to
mind)?
With that approach, all of that code would be permanently be stuck in
no-specialization land (in the sense that even if the HKT couldn't be expressed
with any-T, individual instantiations could ... but not under the scheme
proposed in this draft).
Manual control over specialization
----------------------------------
Maybe this chapter has some merit, but I feel that the reasons put forward are
not very convincing:
"Implementation by parts" argues a point which I'd consider a neat hack for a
single use-case. In a language/world were Option was a value type, why would we
even bother to pass Option(null)/Option(value) instead of null/value (in those
simple cases)?
"Overriding specific instantiations of specialized classes" might have some
merit, but the example of ArrayList<boolean> seems to be an incredibly poor
choice to drive home the argument:
[...] the implementation should pack the elements so that every bool only uses
one bit of memory. This is widely considered a mistake.
–– Wikipedia on C++' vector<bool>
Especially in everything-is-mutable-Java, having to lock 7 other values to
update a single value doesn't seem to be a design decision fit for a general
purpose class. (So we need some kind of BitSet, and then we can just implement
it any way we want without needing this language feature.)
"Specializing the specializations"
I'm not sure this belongs here. Whether you want to add typeclasses to the
language should be orthogonal to the topic of "manual control over
specialization".
"Overriding specific instantiations of generic methods"
Valid point.
The "Peeling" Technique⁴:
-------------------------
To be honest, this feels like the draft is trying to fix one case of poor
library
design by adding a hugely complex language feature.
Especially from a user perspective, this is probably as complicated as the rest
of specialized generics plus value types combined.
Having just read it again ... really, this is so absurdly complex for basically
trying to work around two poor API decisions. That's a huge chunk from the
complexity budget which I'd either spend on something more worthwhile–or not
spend at all.
Yes, one might argue that there could be other libraries out there which might
have comparable issues, but you have to consider that most libraries out there
don't follow the standard library's compatibility policy. Compatibility is
regularly broken in major releases and it is usually not a big issue.
If Oracle announces that in a very rare case things need to be adapted slightly
(just as with every release Sun/Oracle ever announced) people will deal with it
(just like with sun.misc.Unsafe going away).
So it basically comes down to two method calls, Map#get and List#remove.
Regarding Map#get:
I'm not seeing an issue here. Returning the zero value for value types might
be not very useful, but that's what happened for reference types for the last
15 years.
I'd argue it's just not a problem which this draft should address.
It's an issue of poor API design. An alternative method has already been
introduced. If it's still such a pressing issue, introduce another method
which returns Option<V> instead of V|null.
I'm not really seeing the issue this draft is supposed to solve here.
Regarding List#remove:
I think alternatives should be considered here.
Wouldn't it be possible to allow the instantiation of List<int>, but disallow
the usage of List#remove by saying that the call is ambiguous?
(Which it technically is!)
In my opinion, something of this complexity shouldn't be put into a language,
just because of an issue which occurs with _one_ method of _one_ interface
given _one_ specific instantiation.
It would be possible to introduce an annotation which is used for the error
message if ambiguation fails, to steer people to the replacement methods
mentioned in the draft. Scala is doing exactly this in implicit resolution,
and it works well.
interface List<any E> {
@AmbiguityErrorMessage("Use `removeByIndex` instead.")
E remove (int index)
E removeByIndex(int index)
@AmbiguityErrorMessage("Use `removeByValue` instead.")
boolean remove (T element)
boolean removeByValue(T element)
}
Summary: The draft looks good overall, but I think the reasons mentioned for
adopting the "peeling technique" are extremely weak, while putting substantial
language complexity in front of users.
Thanks and bye,
Simon
¹
http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html#language-restrictions
² http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html#statics
³
http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html#manual-control-over-specialization
⁴
http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html#the-peeling-technique
More information about the valhalla-dev
mailing list