[sip 15] value classes and arrays take 2

157 views
Skip to first unread message

martin odersky

unread,
Apr 23, 2012, 10:36:17 AM4/23/12
to scala...@googlegroups.com
[Fixed some typos that could have led to confusion]

There was some argument that we would like instances of value classes to be represented in unboxed form in arrays. Here's an exploration what this would mean. I am assuming we represent arrays as we represent them now. That is, monomorphic arrays are represented as in Java and polymorphic arrays are not wrapped or copied. We tried with great ingenuity to do otherwise in Scala up to 2.7 and failed. So let's not get back into that can of worms again.

Now, consider:

1:   val v: V = new V(1.0)
2:   val a: Array[V] = Array(v)
3:   def f[T](b: Array[T]) = b(0) isInstanceOf V

If `a` in line 2 is represented as an array of unboxed doubles, then there is no way we can distinguish b(0) from a double in the instanceof test in line 3. In other words, elements of value classes would behave differently from all other elements when it comes to type tests and pattern matches.

So to summarize: If we go for a kind of value classes that can be unboxed in arrays, these

 - cannot extend any traits
 - cannot override toString, equals, or hashCode
 - cannot support pattern matching or type tests

The last restriction means that we'd probably need some new declaration syntax for this new kind of value classes. They are clearly not the value classes specified in SIP 15. Probably not even call these types classes, because we seem to violate some fundamental aspects of classes, namely that they have a runtime type. In the following, I'll call them value newtypes instead.

So, for me, value classes and value newtypes have quite different properties. 

Only value classes make sense as implicit wrappers.
Only value classes can be seen as an optimization of plain classes.
Only value newtypes can be unboxed in arrays.

My inclination is to do value classes now, concentrate on making them work with specialization, and then evaluate whether FlatArrays are 
the right answer to storing them compactly in sequences. If that's not the case, we could investigate value newtypes as an alternative.

Your thoughts?

Cheers

 - Martin





--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967

Erik Osheim

unread,
Apr 23, 2012, 10:50:50 AM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 04:36:17PM +0200, martin odersky wrote:
> So, for me, value classes and value newtypes have quite different
> properties.
>
> Only value classes make sense as implicit wrappers.
> Only value classes can be seen as an optimization of plain classes.
> Only value newtypes can be unboxed in arrays.
>
> My inclination is to do value classes now, concentrate on making them work
> with specialization, and then evaluate whether FlatArrays are
> the right answer to storing them compactly in sequences. If that's not the
> case, we could investigate value newtypes as an alternative.
>
> Your thoughts?

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.

Eugene Burmako

unread,
Apr 23, 2012, 10:53:15 AM4/23/12
to scala...@googlegroups.com
Maybe we can save the situation with type tags?

E.g. give a static error if the user tries to pass an array of value classes into an untagged polymorphic method, and utilize a tag for statically unresolvable operations (much like we plan to utilize tags for pattern matching on abstract types).

Ismael Juma

unread,
Apr 23, 2012, 11:00:06 AM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 3:50 PM, Erik Osheim <er...@plastic-idolatry.com> wrote:
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?


Best,
Ismael

Paul Phillips

unread,
Apr 23, 2012, 11:01:01 AM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 7:53 AM, Eugene Burmako <eugene....@epfl.ch> wrote:
> Maybe we can save the situation with type tags?
>
> E.g. give a static error if the user tries to pass an array of value classes
> into an untagged polymorphic method, and utilize a tag for statically
> unresolvable operations (much like we plan to utilize tags for pattern
> matching on abstract types).

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.

Erik Osheim

unread,
Apr 23, 2012, 11:07:55 AM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 04:00:06PM +0100, Ismael Juma wrote:
> Have you seen:
>
> https://github.com/jbellis/jamm

I had not seen this. Thanks!

-- Erik

martin odersky

unread,
Apr 23, 2012, 11:11:06 AM4/23/12
to scala...@googlegroups.com
Correct, as long as we cannot specialize. Once we can specialize value classes, we will be able to avoid the boxings and unboxings and have FlatArray as fast as an array of the unboxed type.

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.

Cheers

 - Martin

Erik Osheim

unread,
Apr 23, 2012, 11:15:57 AM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 05:11:06PM +0200, martin odersky wrote:
> 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.

Agreed. I'd rather wait and ship FlatArray when it performs well.

-- Erik

Simon Ochsenreither

unread,
Apr 23, 2012, 11:21:03 AM4/23/12
to scala...@googlegroups.com
Hi everyone,

isn't this a situation where everyone should start staring at Oracle for a real fix?

For clarity, I'm not suggesting that we wait until they ship with a JVM supporting this feature.
But including something into the language which is more or less a huge workaround for JVM limitations without any perspective whether this clutch will need to remain forever concerns me.

It is not like this problem is limited to Scala: Every decent language will hit it sooner or later. Wouldn't it be possible to work out a proposal (probably combine forces with other languages running on the JVM) and submit it to http://openjdk.java.net/jeps/0, so there is at least a tentative perspective on how long people have to live with these workarounds?

Thanks and bye,


Simon

Simon Ochsenreither

unread,
Apr 23, 2012, 11:23:08 AM4/23/12
to scala...@googlegroups.com
I think holding back would be better.

Bye,

Simon

Ismael Juma

unread,
Apr 23, 2012, 11:25:47 AM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 4:11 PM, martin odersky <martin....@epfl.ch> wrote:
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.

Hold back, please.

Best,
Ismael

Jorge Ortiz

unread,
Apr 23, 2012, 11:34:08 AM4/23/12
to scala...@googlegroups.com
Perhaps Scala could benefit from match-like and isInstanceOf-like constructs that require type tags and overcome erasure?

Kevin Wright

unread,
Apr 23, 2012, 11:44:50 AM4/23/12
to scala...@googlegroups.com
My understanding is that part of the motivation behind vitpatmat is the fact that it *can* be modified to take advantage of TypeTags (where available).  So there's no need to add a new match-like construct when the existing one can already be upgraded to have more smarts.
--
Kevin Wright
mail: kevin....@scalatechnology.com
gtalk / msn : kev.lee...@gmail.com
vibe / skype: kev.lee.wright
steam: kev_lee_wright

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

Paul Phillips

unread,
Apr 23, 2012, 11:45:08 AM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 8:34 AM, Jorge Ortiz <jorge...@gmail.com> wrote:
> Perhaps Scala could benefit from match-like and isInstanceOf-like constructs
> that require type tags and overcome erasure?

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.

martin odersky

unread,
Apr 23, 2012, 12:05:22 PM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 5:34 PM, Jorge Ortiz <jorge...@gmail.com> wrote:
Perhaps Scala could benefit from match-like and isInstanceOf-like constructs that require type tags and overcome erasure?

It would and it will. But the construct we will have in virtpatmatch and 2.10 goes the other way:

Given a type test    x isInstanceOf T   where T is a type variable or contains type variables, we can still do the test as long as T is Typeable (newspeak for T has a TypeTag; TypeTag will be renamed to Typeable).

There's nothing that can make use of more info on the side of x. It's also hard to see how to do such a thing if a is an Any. So, just to throw a spammer in the works here's my example reformulated so that all hopes of using Typable are lost:

1:   val v: V = new V(1.0)
2:   val a: Array[V] = Array(v)
3:   def f[T](b: Array[T]) = { val b: Any = b(0); b isInstanceOf V }

Cheers

 -- Martin


Jeff Olson

unread,
Apr 25, 2012, 6:03:20 PM4/25/12
to scala...@googlegroups.com

On Monday, April 23, 2012 10:11:06 AM UTC-5, Martin wrote:

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.



I'm fine with leaving out FlatArray until the issues with specialization are solved. I would probably avoid using it until such time anyway (unless testing convinced me that escape analysis was able to do away with the boxing).

As to the value newtypes: they seem of limited-to-no value to me--at least without some sort of type-safe equals. The 3.feet == 3.meter problem seems to kill any possible use-case I can think of.

Leonid Ilyevsky

unread,
Apr 26, 2012, 5:33:38 PM4/26/12
to scala-sips
Maybe indeed it would be useful to compare with other languages.
Look at "struct" in X10. No boxing/unboxing, they always live as
simple structures, and are copied by value.

On Apr 23, 12:05 pm, martin odersky <martin.oder...@epfl.ch> wrote:
Reply all
Reply to author
Forward
0 new messages