Hi Martin,
I agree that you've hit upon an important distinction: value classes
are basically just "optimized" classes that work better as implicit
wrappers, and in some other cases where they will be short-lived.
By contrast, newtypes are what you'd want when you want to "add" stuff
(i.e. methods) to existing value types or create more stringent type
restrictions while being assured that the values will never be boxed.
This is a good way to divide up the problem, and I think it's worth
being clear on the use cases SIP-15 is most concerned with (cases 1-2,
I think).
I would advocate against including FlatArray in the standard library,
if only because I expect most people are more concerned with
boxing/unboxing time overhead than with space (there are already lots
of good ways to reduce memory footprint using serialization, Basis,
ByteBuffer, etc, but all of those have a cost in time).
Array[V] won't be any slower than FlatArray[V] due to boxing and
unboxing [1]. In fact Array[Double] with manually conversions from V
will be much faster (avoiding boxing). So, I would rather leave
FlatArray out of the standard library and let people make these kinds
of trade-offs for themselves (since I don't think there is a "one size
fits all" solution).
-- Erik
[1] I was planning on uploading some Caliper benchmarks to Github,
comparing primitives, value classes, and regular classes. I don't have
a good sense for how to write benchmarks in Java that measure space
instead of time, although presumably I can just create some big data
structures and look at overall memory usage? Suggestions would be
welcome here.
I don't have a good sense for how to write benchmarks in Java that measure spaceinstead of time, although presumably I can just create some big data
structures and look at overall memory usage?
I was in the process of suggesting something like this, but I don't
think it is enough, not if we are determined to hang onto the ability
to distinguish Foos from Doubles at runtime.
def f(x: Any) = x match {
case xs: Array[Double] => xs(0)
}
So that's going to come up with a Double whether the array you passed
it is an Array[Double] or any other. Without some external means of
distinguishing these (keep a map on creation? crazy!) one Double will
look pretty much like another.
I had not seen this. Thanks!
-- Erik
Agreed. I'd rather wait and ship FlatArray when it performs well.
-- Erik
So, the question is: Do we include FlatArray now, so that people can get the optimizations automatically later once specialization is on? (probably would require a recompile, though). Or do we hold back, because the current version does not have any benefits? I am happy with either course of action, with slight preference to hold back.
It has long been the plan that said match-like and instanceOf-like
constructs would be match and instanceOf, and they would use the
Manifest (as we called it in those days, when I wore an onion on my
belt) if available, e.g.
def f[T: Manifest](xs: List[T]) = xs match { case _: List[Int] =>
true ; case _ => false }
This would stop emitting unchecked warnings and start emitting correct
answers instead.
But as you appear to be observing, we can't escape the noose if we use
match, because we still have this problem of
x match { case xs: Array[Double] => xs(0) }
matching things which we want not to be considered as Array of Double.
So "supermatch" requires a tag, from everyone - including
Array[Double] - and then they can be distinguished.
Seems worthy of exploration.
Perhaps Scala could benefit from match-like and isInstanceOf-like constructs that require type tags and overcome erasure?
So, the question is: Do we include FlatArray now, so that people can get the optimizations automatically later once specialization is on? (probably would require a recompile, though). Or do we hold back, because the current version does not have any benefits? I am happy with either course of action, with slight preference to hold back.