Making Option a value class

962 views
Skip to first unread message

Jesper Nordenberg

unread,
Mar 12, 2013, 6:35:53 PM3/12/13
to scala-l...@googlegroups.com
I've done some testing with an Option value class and it seems to work
well. In most use cases I think this would save quite a bit of memory
and a lot of object allocations. In the, I assume, less common case of
up-casting None to a super type it will consume more memory than the
current Option type and entail boxing. Here's my short blog post about it:

http://jnordenberg.blogspot.se/2013/03/a-more-efficient-option.html

I think this is worth considering to make scala.Option a value class
unless some major problems are discovered.

/Jesper Nordenberg

Alex Kravets

unread,
Mar 12, 2013, 6:41:08 PM3/12/13
to scala-l...@googlegroups.com
I agree with this post.  I saw dramatic memory improvements by introducing an auto-create an Option on demand and return it from an API optimization, rather than create and store a ref to an extra object in a large scala code base.




/Jesper Nordenberg

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.





--
Alex Kravets       def redPill = 'Scala
[[ brutal honesty is the best policy ]]

Arya Irani

unread,
Mar 12, 2013, 6:43:50 PM3/12/13
to scala-l...@googlegroups.com
The main problem would be breaking backwards compatibility; its super type changes from AnyRef to AnyVal.  That being said, I'd welcome the change.

P.S.  Is there any discussion if Java interop with value classes?
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

Jesper Nordenberg

unread,
Mar 12, 2013, 6:52:43 PM3/12/13
to scala-l...@googlegroups.com, Arya Irani
Yes, that would be a major problem. A better strategy might be to add a
new option type (OptionVal?) to the stdlib and add first class support
for it in the pattern matcher.

/Jesper Nordenberg

Arya Irani skrev 2013-03-12 23:43:
> The main problem would be breaking backwards compatibility; its super
> type changes from AnyRef to AnyVal. That being said, I'd welcome the
> change.
>
> P.S. Is there any discussion if Java interop with value classes?
>
> On Tuesday, March 12, 2013, Jesper Nordenberg wrote:
>
> I've done some testing with an Option value class and it seems to
> work well. In most use cases I think this would save quite a bit of
> memory and a lot of object allocations. In the, I assume, less
> common case of up-casting None to a super type it will consume more
> memory than the current Option type and entail boxing. Here's my
> short blog post about it:
>
> http://jnordenberg.blogspot.__se/2013/03/a-more-efficient-__option.html

Stephen Compall

unread,
Mar 12, 2013, 8:11:23 PM3/12/13
to scala-l...@googlegroups.com
On Tue, 2013-03-12 at 23:35 +0100, Jesper Nordenberg wrote:
> I think this is worth considering to make scala.Option a value class
> unless some major problems are discovered.

Welcome to Scala version 2.10.1-RC3 (OpenJDK 64-Bit Server VM, Java 1.7.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala> final case class MyOption[+T](value: Any = null)
extends AnyVal with Product with Serializable
defined class MyOption

scala> def idO[A]: MyOption[A] => MyOption[A] = a => a
<console>:9: error: bridge generated for member method apply:
(a: MyOption[A])MyOption[A] in anonymous class $anonfun
which overrides method apply: (v1: T1)R in trait Function1
clashes with definition of the member itself;
both have erased type (v1: Object)Object
def idO[A]: MyOption[A] => MyOption[A] = a => a
^

( https://issues.scala-lang.org/browse/SI-6260 )

There are other major problems, aside from "can't write functions on
them". For one thing, with some o:MyOption[...], f, g, o.map(f andThen
g) != o.map(f).map(g), which is somewhat counterintuitive.

You can see much more discussion on this sort of thing from last July:
https://groups.google.com/d/topic/scala-internals/1DXdknjt9pY/discussion

--
Stephen Compall
"^aCollection allSatisfy: [:each | aCondition]": less is better than


Erik Osheim

unread,
Mar 12, 2013, 8:12:09 PM3/12/13
to scala-l...@googlegroups.com, Arya Irani
On Tue, Mar 12, 2013 at 11:52:43PM +0100, Jesper Nordenberg wrote:
> Yes, that would be a major problem. A better strategy might be to
> add a new option type (OptionVal?) to the stdlib and add first class
> support for it in the pattern matcher.

The other advantage to adding a new type is that there is currently
code in the wild that depends on being able to create Some(null). :/

(At least, last time this came up that was considered a big stumbling
block I think.)

-- Erik

Simon Ochsenreither

unread,
Mar 12, 2013, 8:34:32 PM3/12/13
to scala-l...@googlegroups.com

I think this is worth considering to make scala.Option a value class
unless some major problems are discovered.

The major problem discovered earlier is that Some(null) doesn't work with value classes. How did you solve that?

Paul Phillips

unread,
Mar 12, 2013, 8:37:55 PM3/12/13
to scala-l...@googlegroups.com, Arya Irani

On Tue, Mar 12, 2013 at 5:12 PM, Erik Osheim <er...@plastic-idolatry.com> wrote:
The other advantage to adding a new type is that there is currently
code in the wild that depends on being able to create Some(null). :/

(At least, last time this came up that was considered a big stumbling
block I think.)

The really big stumbling block is that Option is entrenched as a means of communicating "maybe there is a result" but also a means of communicating "it is not null", and these are not the same thing, neither interchangeable nor unifiable in a space of one bit.

Assuming Option is a value class and new types are not introduced, then either:

a) the bit means "it is not null", and you can't have signatures like def find(f: A => Boolean): Option[A]
b) the bit means "it is not there", and the collections cannot contain null
c) the meaning of the bit shifts unpredictably with the winds

It is probably unrealistic to save Option. I would target a new type - and make sure the meaning of its one bit is easily identified.

Jesper Nordenberg

unread,
Mar 13, 2013, 3:22:54 AM3/13/13
to scala-l...@googlegroups.com, Simon Ochsenreither
Simon Ochsenreither skrev 2013-03-13 01:34:
> The major problem discovered earlier is that Some(null) doesn't work
> with value classes. How did you solve that?

Well, obviosuly Some(null) == None. Probably some people rely on
Some(null) != None (which is sorta weird IMHO as None is meant to
replace null) which would be a major problem.

/Jesper Nordenberg

Paul Phillips

unread,
Mar 13, 2013, 3:27:04 AM3/13/13
to scala-l...@googlegroups.com, Simon Ochsenreither

On Wed, Mar 13, 2013 at 12:22 AM, Jesper Nordenberg <mega...@yahoo.com> wrote:
Well, obviosuly Some(null) == None. Probably some people rely on Some(null) != None (which is sorta weird IMHO as None is meant to replace null) which would be a major problem.

Some people like the standard library.

Jesper Nordenberg

unread,
Mar 13, 2013, 3:35:12 AM3/13/13
to scala-l...@googlegroups.com, Stephen Compall
Stephen Compall skrev 2013-03-13 01:11:
> scala> final case class MyOption[+T](value: Any = null)
> extends AnyVal with Product with Serializable
> defined class MyOption
>
> scala> def idO[A]: MyOption[A] => MyOption[A] = a => a
> <console>:9: error: bridge generated for member method apply:
> (a: MyOption[A])MyOption[A] in anonymous class $anonfun
> which overrides method apply: (v1: T1)R in trait Function1
> clashes with definition of the member itself;
> both have erased type (v1: Object)Object
> def idO[A]: MyOption[A] => MyOption[A] = a => a
> ^
>
> ( https://issues.scala-lang.org/browse/SI-6260 )

I ran into a similar error when using orElse on my unboxed option type.
It should be fixable though, right?

> You can see much more discussion on this sort of thing from last July:
> https://groups.google.com/d/topic/scala-internals/1DXdknjt9pY/discussion

I wasn't aware this suggestion had been discussed in detail before.
Thanks for the link, I'll look into it.

I still think it would be a good thing (TM) to have an unboxed option
type in the stdlib, but replacing scala.Option would currently probably
be too much of a hassle.

/Jesper Nordenberg


Jesper Nordenberg

unread,
Mar 13, 2013, 3:49:12 AM3/13/13
to scala-l...@googlegroups.com, Paul Phillips, Arya Irani
Paul Phillips skrev 2013-03-13 01:37:
> The really big stumbling block is that Option is entrenched as a means
> of communicating "maybe there is a result" but also a means of
> communicating "it is not null", and these are not the same thing,
> neither interchangeable nor unifiable in a space of one bit.

Yes, maybe it's better to create a new value class that only targets the
concept of nullability and provide reasonable conversion methods between
Option and this type. If we add support for postfix type syntax we can
steal Kotlin's nice syntax for this nullable type: String?

/Jesper Nordenberg

Simon Ochsenreither

unread,
Mar 13, 2013, 8:40:08 AM3/13/13
to scala-l...@googlegroups.com, Paul Phillips, Arya Irani

If we add support for postfix type syntax we can
steal Kotlin's nice syntax for this nullable type: String?

That would be horrible.

Paul Phillips

unread,
Mar 13, 2013, 9:30:09 AM3/13/13
to Simon Ochsenreither, scala-l...@googlegroups.com, Arya Irani

On Wed, Mar 13, 2013 at 5:40 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
That would be horrible.

I'm with Simon on this one. I don't think a hail of interrogation points would improve my programming experience.

Nils Kilden-Pedersen

unread,
Mar 13, 2013, 10:14:55 AM3/13/13
to scala-l...@googlegroups.com, Arya Irani
On Tue, Mar 12, 2013 at 5:52 PM, Jesper Nordenberg <mega...@yahoo.com> wrote:
Yes, that would be a major problem. A better strategy might be to add a new option type (OptionVal?)

If this was a democracy, and I had a vote, I'd vote for Opt.
 
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+unsubscribe@googlegroups.com.

Rich Oliver

unread,
Mar 13, 2013, 1:00:03 PM3/13/13
to scala-l...@googlegroups.com, Arya Irani
On Wednesday, March 13, 2013 2:14:55 PM UTC, Nils wrote:

If this was a democracy, and I had a vote, I'd vote for Opt.

'The Gaffer he says: "Make it short and then you won't have to cut it short before you use it." - Sam Gangee quoting the Gaffer on names in Lord of the Rings - Scouring of the Shire.

Ben Hutchison

unread,
Mar 13, 2013, 6:31:58 PM3/13/13
to scala-l...@googlegroups.com, Paul Phillips, Arya Irani
On Wed, Mar 13, 2013 at 6:49 PM, Jesper Nordenberg <mega...@yahoo.com> wrote:
Paul Phillips skrev 2013-03-13 01:37:

The really big stumbling block is that Option is entrenched as a means
of communicating "maybe there is a result" but also a means of
communicating "it is not null", and these are not the same thing,
neither interchangeable nor unifiable in a space of one bit.

Yes, maybe it's better to create a new value class that only targets the concept of nullability and provide reasonable conversion methods between Option and this type.

+1.

While there may be compatibility problems that sink a wholesale replacement of the current Option with a value class, Jepser's essential point remains sensible; with broad adoption over time, an Option-like value class would be a big win for Scala performance. 

Can we talk about how to make it happen, at least as much as pointing out the obstacles?

Personally I'd rather the core team take the lead and put something in the standard library, *once*, that we can all use, than see it re-implemented in multiple community libraries. (Monoid being an example of this tendency). And only if it's in the core, do we get the ability to extend Collections API in future to take advantage of it.

-Ben

 
If we add support for postfix type syntax we can steal Kotlin's nice syntax for this nullable type: String?

/Jesper Nordenberg
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+unsubscribe@googlegroups.com.

Ben Hutchison

unread,
Mar 13, 2013, 6:48:18 PM3/13/13
to scala-l...@googlegroups.com
On Thu, Mar 14, 2013 at 1:14 AM, Nils Kilden-Pedersen <nil...@gmail.com> wrote:
On Tue, Mar 12, 2013 at 5:52 PM, Jesper Nordenberg <mega...@yahoo.com> wrote:
Yes, that would be a major problem. A better strategy might be to add a new option type (OptionVal?)

If this was a democracy, and I had a vote, I'd vote for Opt.

+1

-Ben

Simon Ochsenreither

unread,
Mar 13, 2013, 8:52:20 PM3/13/13
to scala-l...@googlegroups.com, Paul Phillips, Arya Irani
In my opinion it's not worth it to have two different types which do nearly the same thing. Not only will the transition be horrible but it would mean adding additional conversion methods to tons of existing classes.
Of course it would be nice to have that optimization, but sometimes one has to make compromises.

Ruslan Shevchenko

unread,
Mar 14, 2013, 2:24:51 AM3/14/13
to scala-l...@googlegroups.com
btw, if we would have something like NotNull trait  (or support applying of type annotations  over existing types)  and  make  
Option(x:X <:AnyRef): Option[X with NotNull]      (or  Option[@NotNull X] in annotation case)
then compiler will be able to do optimization, rely on value representation of Option[_ <: AnyRef with NotNulll] behind the scene
 


On Thu, Mar 14, 2013 at 2:52 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
In my opinion it's not worth it to have two different types which do nearly the same thing. Not only will the transition be horrible but it would mean adding additional conversion methods to tons of existing classes.
Of course it would be nice to have that optimization, but sometimes one has to make compromises.

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

Ruslan Shevchenko

unread,
Mar 14, 2013, 2:31:37 AM3/14/13
to scala-l...@googlegroups.com
of course in case of current inheritance semantics:  this must be something like Option [_ <% NotNull ] 
   

Simon Ochsenreither

unread,
Mar 14, 2013, 10:05:41 AM3/14/13
to scala-l...@googlegroups.com
That seems like an option!
Let's hope that the Java people get their JSR-308 stuff done in this decade, so we could also use that.

Scott Carey

unread,
Mar 15, 2013, 12:09:11 AM3/15/13
to scala-l...@googlegroups.com
I've been thinking about Option, its boxing, and some related things for a while.   Before Value Classes, I had a few ideas, and eventually dug up some older conversations here.  However, I have not gone through all of those quite yet.  "Down With Option!"  "Long Live Option!"

The below ideas are clearly not a minor tweak to Scala, but perhaps some bits of it may be useful, maybe for Scala 3.x.  Or it might give someone who knows a lot more about this stuff than I do some ideas.

I will use some scala-like, haskell-like syntax below, but do not assume that any similarity to these implies exact analogies with these languages as they stand, it is merely to help demonstrate my thoughts.

First, I see some relationship between the desire to have an non-boxing Option (or Either, or Try, etc) and what some may call Union types (but perhaps not in the pure academic sense).  Perhaps you could use the '|' character to symbolize an or relationship between types and say something like:

type Opt[A] = A | Nothing {
  def getOrElse(a : A) : A = ...
}

To define Opt[A] as a synonym for the union of type A and type Nothing

Anyone could chose to explicitly or implicitly convert the type 'A | Nothing' to an Option[A] and call getOrElse.   The compiler could even take type 'A | B | Nothing' and interpret it as 'Option[A | B]'. 

If another union type existed:
type Either[A , B] = A | B

so one could easily convert without boxing 'A | B | Nothing' to Option[Either[A, B]] or Either[A, Option[B]] or Either[Option[A], B]  (this gets tricky and ambiguous, obviously, if implicit rather than explicit)

In other words, perhaps there exists a general solution to having the compiler to map a type and its methods in some context to a union of types that matches its definition.

But what about null?  Well, perhaps null can be removed from Scala (3.x ?), represented by Nothing.  It has been said it is there for Java compatibility, which can be achieved with these same type unions (Nothing passed to Java is java null, and vice-versa).

If there were some magical way for Scala to know a method or function signature was from Scala, it could identify signatures that might return null and define the return type as a union.  A method from a Java library that had the signature:

X convert(Y y) { ... }

Would appear to Scala as:

def convert(y: Nothing | Y) : Nothing | X = { ... }

to indicate that it might return Nothing or X and can take Y or Nothing, and an in scope implicit for Opt would mean you could simply use it like:

convert(y).getOrElse(defaultX)

A Java spec for annotating methods with @Nullable or @NonNull could be used to streamline the inter-language bits.


Value classes are clearly a big step towards limiting boxing when the compiler clearly has all the information necessary and holding an object wrapper at runtime is pure waste, but my gut is telling me there is more that can be done, in a general way, and that it is related to being able to define type unions, but I know too little about the more advanced bits of Scala and its related type theory to call BS on myself.

Am I crazy?

Vasya Novikov

unread,
Mar 29, 2013, 8:44:06 AM3/29/13
to scala-l...@googlegroups.com
I find this value class Option very interesting, too.

I'd rather see standard library changes than have a performance loss forever. (Especially taking the fact that there will be no difference for non-null collections.)

Eric Springer

unread,
Mar 30, 2013, 11:52:40 AM3/30/13
to scala-l...@googlegroups.com, Arya Irani


On Wednesday, March 13, 2013 7:14:55 AM UTC-7, Nils wrote:
If this was a democracy, and I had a vote, I'd vote for Opt.

+1, this would be great -- even more so if with a refactoring tool to find/replace Option. The status quo with is (imho) embarrassing -- this change needs to happen. So we might as well do it sooner than later. And as for the name, I even prefer Opt to Option :D

Paul Phillips

unread,
Mar 30, 2013, 12:20:29 PM3/30/13
to scala-l...@googlegroups.com, Arya Irani
On Sat, Mar 30, 2013 at 8:52 AM, Eric Springer <ericws...@gmail.com> wrote:
+1, this would be great -- even more so if with a refactoring tool to find/replace Option. The status quo with is (imho) embarrassing -- this change needs to happen. So we might as well do it sooner than later. And as for the name, I even prefer Opt to Option :D

It would be a big mistake to homestead on the last bit of appealing namespace without addressing the issue I bring up whenever this topic comes by. What exactly does "optional" mean? If it has multiple meanings, what does it mean when people compose Opts with different meanings? (And why are they composable?) What should be the behavior of the Opt equivalent of Some(null) ? What does Opt[T] mean if T >: Null? 
 
  def find[T](xs: List[T], p: T => Boolean): Opt[T]
  find(List[String](null, ""), _ != "bippy")

What would this do? Does the Some(null) magically into OptNone? Throw an exception?

Rex Kerr

unread,
Mar 30, 2013, 12:42:10 PM3/30/13
to scala-l...@googlegroups.com, Arya Irani

For value-class option, this is really easy: Some(null) is an error.  The contents of Some is NotNull.  So if you have a collection that _can_ store Null, don't expect to get it with Opt.  Put some sentinel in your collection instead of null if you (optionally) want to get the sentinel back out again.

Note that this also means that List[Opt[T]] will be unable to find any instances of None in the list, as they'll be null.

Is this a problem?  Maybe.  Boxes are useful because they can be empty, and you can count how many layers of wrapping you have.  Lightweight options do not solve all problems.  Even if you solve the Null problem with some sort of sentinel scheme, you have the how-boxed-am-I problem which cannot be solved that way.  It does not follow that lightweight options should be abandoned, just that they might not be a universal panacea.

  --Rex

Paul Phillips

unread,
Mar 30, 2013, 2:41:28 PM3/30/13
to scala-l...@googlegroups.com, Arya Irani
On Sat, Mar 30, 2013 at 9:42 AM, Rex Kerr <ich...@gmail.com> wrote:
It does not follow that lightweight options should be abandoned, just that they might not be a universal panacea.

I'm pretty sure nobody suggested abandonment. What I said - still true - is don't rush into it without clearly addressing these matters.

Rex Kerr

unread,
Mar 30, 2013, 2:46:26 PM3/30/13
to scala-l...@googlegroups.com, Arya Irani
Agreed.  The distinction between Option[A] and Opt[A <: NotNull] should be appreciated before difficult-to-reverse changes (or a great deal of work) are completed.

  --Rex

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

Simon Ochsenreither

unread,
Mar 31, 2013, 6:29:19 AM3/31/13
to scala-l...@googlegroups.com, Arya Irani
All this stuff sounds pretty much like a job for the runtime. I'm not willing to further complicate/uglify the Option/Either/Try/Validation space, just because HotSpot people can't get anything done.

Nothing prevents a runtime from automatically applying this optimization for value classes.

Josh Suereth

unread,
Mar 31, 2013, 8:41:27 AM3/31/13
to scala-l...@googlegroups.com, Arya Irani

BTW -  random thoughts from the past ->

1. It is the meaning of null that developers are confused about.  Some take it to mean "exists" or "not exists" and so it conflates our option opinions (is it a way to stop using null?  Well it should be for the exists/not-exists use case)

2.  We had thought at one time about the following:

trait Option[+T] extends Any
trait OptVal[+T <: NotNull] extends Option [T]
case class SomeVal[T](value: T) extends AnyVal with OptVal[T]
case object SomeNull extends Option[Null]
case object None extends Option[Nothing]
object Some // extractor and unapply

Which I think still fails to give the appropriate benefits and adds a fun level of confusion.   Did we ever dig into this option further?

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

Grzegorz Kossakowski

unread,
Mar 31, 2013, 1:25:17 PM3/31/13
to scala-l...@googlegroups.com, Arya Irani
On 31 March 2013 03:29, Simon Ochsenreither <simon.och...@gmail.com> wrote:
All this stuff sounds pretty much like a job for the runtime. I'm not willing to further complicate/uglify the Option/Either/Try/Validation space, just because HotSpot people can't get anything done.

Simon,

I'd appreciate if you could use words that do not offend anybody. I have a lot of respect for HotSpot people because HotSpot JVM is still the fastest JVM that runs Scala bytecode in most common scenarios. Also, HotSpot team is free to chose priorities they want and offending them won't help to change their mind.

--
Grzegorz Kossakowski
Scalac hacker at Typesafe
twitter: @gkossakowski

Simon Ochsenreither

unread,
Mar 31, 2013, 6:12:19 PM3/31/13
to scala-l...@googlegroups.com, Arya Irani
Sorry, I'll shut up.

Vasya Novikov

unread,
Apr 1, 2013, 5:44:39 PM4/1/13
to scala-l...@googlegroups.com, Arya Irani
I don't actually understand this.. How can JVM optimize Option[Data]  if it has no knowledge whether the inner data is null ?

There is also another way, if altering Option is not wanted yet. Maybe add a "Nullable[T]" class? The same as was proposed here, just added to standard collection methods. This way you won't break existing code. And the name "Nullable", I hope, will give people a hint that it deals with "nulls".

Rüdiger Klaehn

unread,
Apr 24, 2013, 6:18:49 PM4/24/13
to scala-l...@googlegroups.com
Hi all,

I think it is clear that if you want to replace scala.Option with a value class, you have to support Some(null). There is just no way around it even if it might be ugly.

Here is a version of Option as a value class that uses a private val to handle the Some(null) case. Other than that it is identical to the version from you.

https://github.com/rklaehn/valueclassoption/tree/master/src/main/scala/scala2

And here is how it works:

scala> import scala2._
import scala2._

scala> val text:String=null
text: String = null

scala> val o = Some(text)
o: scala2.Option[String] = Some(null)

scala> o match {
     |   case Some(value) => println(value)
     |   case None => println("None")
     | }
null

I have no idea of the performance implications of the additional check in some(x) and get, but they should not be too high.

On Tue, Mar 12, 2013 at 11:35 PM, Jesper Nordenberg <mega...@yahoo.com> wrote:
I've done some testing with an Option value class and it seems to work well. In most use cases I think this would save quite a bit of memory and a lot of object allocations. In the, I assume, less common case of up-casting None to a super type it will consume more memory than the current Option type and entail boxing. Here's my short blog post about it:

http://jnordenberg.blogspot.se/2013/03/a-more-efficient-option.html

I think this is worth considering to make scala.Option a value class unless some major problems are discovered.


/Jesper Nordenberg

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+unsubscribe@googlegroups.com.

Chris Hodapp

unread,
Apr 24, 2013, 8:34:19 PM4/24/13
to scala-l...@googlegroups.com
I note that, if you weren't particular about what sort of exception were thrown, get could be just value.asInstanceOf[T] for any T other than Any, AnyVal, or AnyRef. I think you are right to be particular, though.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

Rüdiger Klaehn

unread,
Apr 25, 2013, 4:00:20 AM4/25/13
to scala-l...@googlegroups.com
Neat idea.

But it has to work for all types to be a drop-in replacement for scala.Option. And in any case throwing a ClassCastException instead of a NoSuchElementException is not really acceptable.

But giving the guard value a special type might be a good idea anyway. That way you use isInstanceOf instead of eq to do the check for None. I read somewhere on this list that isInstanceOf can be faster than an eq check with a val defined in a companion object.

But I guess the first thing I have to do is write a microbenchmark. Something like

val x = (0 until 1000).toSeq
def even(x:Int) = if(x%2==0) Some(x) else None
val y = x.flatMap(even)

Does anybody have a microbenchmarking tool that is a bit nicer to use from scala than caliper? Caliper is really a bit annoying.

Sébastien Doeraene

unread,
Apr 25, 2013, 4:02:03 AM4/25/13
to scala-l...@googlegroups.com
Hi,

Hey, nice one :-p I think you can improve performance a bit if you define noneValue as a private object rather than a private val. Being a top-level module (object), the code to load it is much shorter (it's a just a Load-Static-Field or something).

Cheers,
Sébastien


On Thu, Apr 25, 2013 at 2:34 AM, Chris Hodapp <clho...@gmail.com> wrote:

Rüdiger Klaehn

unread,
Apr 25, 2013, 5:38:29 AM4/25/13
to scala-l...@googlegroups.com
On Thu, Apr 25, 2013 at 10:02 AM, Sébastien Doeraene <sjrdo...@gmail.com> wrote:
Hi,

Hey, nice one :-p I think you can improve performance a bit if you define noneValue as a private object rather than a private val. Being a top-level module (object), the code to load it is much shorter (it's a just a Load-Static-Field or something).

Thanks. I didn't know that.
 
I will have a look at the byte code when I have some time. What is the best way to look at the byte code of a value class? The whole point of a value class is that it kind of vanishes at runtime if used properly. So use it in a method of another class and watch the generated code?

 

Paul Phillips

unread,
Apr 25, 2013, 5:49:07 AM4/25/13
to scala-l...@googlegroups.com
On Thu, Apr 25, 2013 at 2:38 AM, Rüdiger Klaehn <rkl...@gmail.com> wrote:
I will have a look at the byte code when I have some time. What is the best way to look at the byte code of a value class? The whole point of a value class is that it kind of vanishes at runtime if used properly. So use it in a method of another class and watch the generated code?

scala> class Bippy(val x: Int) extends AnyVal { def add(that: Bippy) = new Bippy(x + that.x) }
defined class Bippy

// This is where you hope the action is
scala> :javap -v Bippy$
[snip]
  public final int add$extension(int, int);
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=2, locals=3, args_size=3
         0: iload_1       
         1: iload_2       
         2: iadd          
         3: ireturn       

// But there also has to be a box for Bippies
scala> :javap -v Bippy
[snip]
public int x();
       0: aload_0       
       1: getfield      #10                 // Field x:I
       4: ireturn       

public int add(int);
       0: getstatic     #20                 // Field Bippy$.MODULE$:LBippy$;
       3: aload_0       
       4: invokevirtual #22                 // Method x:()I
       7: iload_1       
       8: invokevirtual #26                 // Method Bippy$.add$extension:(II)I
      11: ireturn       
 

Paul Phillips

unread,
Apr 25, 2013, 6:59:11 AM4/25/13
to scala-l...@googlegroups.com
Also, in case anyone thinks this is just a matter of writing Option, you had better take a look at this, because you will never see an AnyVal-derived Option unless it is solved.


You can see this poses a slight problem for Option:

final class Option[+A](val value: A) extends AnyVal

abstract class Foo[A]                { def f(): Option[A] }
         class Bar[A] extends Foo[A] { def f(): Option[A] = ??? }

a.scala:7: error: bridge generated for member method f: ()Option[A] in class Bar
which overrides method f: ()Option[A] in class Foo
clashes with definition of the member itself;
both have erased type ()Object
         class Bar[A] extends Foo[A] { def f(): Option[A] = ??? }
                                           ^
one error found

Rex Kerr

unread,
Apr 25, 2013, 7:53:46 AM4/25/13
to scala-l...@googlegroups.com
Sort of off-topic, but:

There is ScalaMeter, but it's sort of a Caliper replacement, so it's not as lightweight as possible (or wasn't last I checked).

I have a library called Thyme which I will release at some point, but it's not ready yet (due to heavy dependencies on a math "library" (i.e. collection of random stuff) I wrote).  I'm happy to send you a jar off-list, though.

If you want some code benchmarked and the timing info printed to the screen, it's (assuming th is an instance of the Thyme class):

th.pbench{
  // Your code goes here
}

And it'll typically return in a fraction of a second (if your test doesn't take too long).  I'm not sure how to get lighter-weight than that.

Anyway, I definitely agree that performance testing is an essential part of Option-replacement ideas.

  --Rex

John Nilsson

unread,
Apr 25, 2013, 3:14:50 PM4/25/13
to scala-l...@googlegroups.com

Hmm, I'm curious. Why would instanceOf be faster than eq?
In the end I would expect both to end up being no more than a pointer comparison. If not I'd say there's a bug somewhere.

If so, and indeed, one pointer comparison is faster than the other, that must then be due to memory locality no? Is it more likely for the pointer to a class to reside in the cpu cache than the pointer to None? Is it possible that a micro benchmark would create that illusion while the opposite is true in more realistic environments?

Or is it that eq actually must involve indirections that instanceOf do not?

BR
John

Paul Phillips

unread,
Apr 25, 2013, 3:30:13 PM4/25/13
to scala-l...@googlegroups.com
On Thu, Apr 25, 2013 at 12:14 PM, John Nilsson <jo...@milsson.nu> wrote:

Hmm, I'm curious. Why would instanceOf be faster than eq?
In the end I would expect both to end up being no more than a pointer comparison. If not I'd say there's a bug somewhere.

That dramatically oversimplifies the environment in which we operate. I don't know that an instance check is faster, but it's sure easy to believe that it is. Just look at them. The fact that the reference check is 3x bigger could be enough to punish you all by itself.

package j;

class Bippy { }

public class J {
  public static final Object bippy = new Object();

  public boolean f(Object x) {
    return x == bippy;
    //  0: aload_1
    //  1: getstatic     #2                  // Field bippy:Ljava/lang/Object;
    //  4: if_acmpne     11
    //  7: iconst_1
    //  8: goto          12
    // 11: iconst_0
    // 12: ireturn
  }
  public boolean g(Object x) {
    return x instanceof Bippy;
    // 0: aload_1
    // 1: instanceof    #3                  // class j/Bippy
    // 4: ireturn
  }
}


Rüdiger Klaehn

unread,
Apr 25, 2013, 3:39:35 PM4/25/13
to scala-l...@googlegroups.com
I think this was the discussion I vaguely remembered: https://groups.google.com/forum/#!msg/scala-internals/ysNnBpYQwCY/lkLneRq-SF0J


With the suggestion of Sébastien to use a private object instead of a private val, the isEmpty method for example looks like this:

  def isEmpty: Boolean = NoneValue eq value.asInstanceOf[AnyRef]

  public final <T extends java/lang/Object> boolean isEmpty$extension(java.lang.Object);
    Code:
       0: getstatic     #30                 // Field scala2/NoneValue$.MODULE$:Lscala2/NoneValue$;
       3: aload_1      
       4: if_acmpne     11
       7: iconst_1     
       8: goto          12
      11: iconst_0     
      12: ireturn 

When I change the code to this:
 
  def isEmpty: Boolean = value.isInstanceOf[NoneValue.type]

I get the following, almost identical bytecode with not an instanceof anywhere to be seen. I have no idea what is going on there. Possibly the compiler emits this code as an optimization.

  public final <T extends java/lang/Object> boolean isEmpty$extension(java.lang.Object);
    Code:
       0: aload_1      
       1: getstatic     #30                 // Field scala2/NoneValue$.MODULE$:Lscala2/NoneValue$;
       4: if_acmpne     11
       7: iconst_1     
       8: goto          12
      11: iconst_0     
      12: ireturn   

Rüdiger Klaehn

unread,
Apr 25, 2013, 3:48:21 PM4/25/13
to scala-l...@googlegroups.com
Thyme sounds very useful. Caliper is very annoying because of the very long runtimes and the fact that it wants you to publish your results to some sort of web application to analyze them.

Do you plan to release it in the near future? For microbenchmarking to be useful for discussions like this, you have to be able to put it all into a small github project to let other people verify/falsify the results. Just putting the .jar in the unmanaged libs directory would be OK, but you have to be able to at least do that.

Paul Phillips

unread,
Apr 25, 2013, 3:58:37 PM4/25/13
to scala-l...@googlegroups.com
On Thu, Apr 25, 2013 at 12:39 PM, Rüdiger Klaehn <rkl...@gmail.com> wrote:
I get the following, almost identical bytecode with not an instanceof anywhere to be seen. I have no idea what is going on there. Possibly the compiler emits this code as an optimization.

It's not an optimization; it's the definition. Wasn't your declaration of NoneValue

  val NoneValue = new Object

What sort of instance check do you imagine it could perform there? "x.isInstanceOf[NoneValue.type]" means "x eq NoneValue"; and there isn't any other way to test that condition. There would be if it were a declared object.

Rex Kerr

unread,
Apr 25, 2013, 4:01:27 PM4/25/13
to scala-l...@googlegroups.com
I'll have it out by Scala Days 2013.

Check out ScalaMeter in the meantime, though, if you want something that is available.

FWIW, I get that a real instanceof is a very tiny bit faster than an eq (~3%), but not on an object.  I'm not sure this sort of thing is worth optimizing unless we find a much bigger benefit.

Do be aware that objects can be uninitialized and checking for this can consume a lot of time depending on whether the JVM can realize it's wasted effort.

  --Rex

Rüdiger Klaehn

unread,
Apr 25, 2013, 4:05:54 PM4/25/13
to scala-l...@googlegroups.com
No, I changed NoneValue to private object NoneValue following the suggestion from Sébastien. So there would be a type to check against.

See latest version at https://github.com/rklaehn/valueclassoption/blob/master/src/main/scala/scala2/Option.scala


By the way: I think I found another issue with value classes. This seems to crash the compiler in scala 2.10.1:

scala> class Test(val value:Int) extends AnyVal {
     |   def x = y
     |   private[this] def y = 3
     | }
error:
     while compiling: <console>
        during phase: extmethods
     library version: version 2.10.1
    compiler version: version 2.10.1


--

Paul Phillips

unread,
Apr 25, 2013, 4:17:35 PM4/25/13
to scala-l...@googlegroups.com
On Thu, Apr 25, 2013 at 1:05 PM, Rüdiger Klaehn <rkl...@gmail.com> wrote:
No, I changed NoneValue to private object NoneValue following the suggestion from Sébastien. So there would be a type to check against.

Okay: to be clear, it would be the emission of an instance check which would be the optimization. It's defined by reference equality.

"By the way: I think I found another issue with value classes"

John Nilsson

unread,
Apr 25, 2013, 4:21:02 PM4/25/13
to scala-l...@googlegroups.com
But there's not telling what kind of micro-ops that innocent looking "instanceof" is expanded to ,and the eq version doesn't really seem to contain anything that wouldn't be trivial for hotspot to handle, so I wouldn’t take the size of the byte code as an indicator for much in this instance.

However when you put it that way I realize that the instanceof is more likely to be based on a load const after some optimization than the eq case. If nothing else because it seems to me to be a more obvious optimization.

BR,
John


Chris Hodapp

unread,
Apr 25, 2013, 5:24:16 PM4/25/13
to scala-l...@googlegroups.com
At this point, why not just throw in the towel and make a class for NoneValue to extend?

Regarding get:

First see:
scala> class C {
     | def cast[T](x: Any) = try { x.asInstanceOf[T] } catch { case cce: ClassCastException => throw new NoSuchElementException("Bad") }
     | }
defined class C

scala> :javap -v C
Compiled from "<console>"
public class C extends java.lang.Object
...
{
public java.lang.Object cast(java.lang.Object);
  Code:
   Stack=3, Locals=3, Args_size=2
   0: aload_1
   1: areturn
   2: astore_2
   3: new #11; //class java/util/NoSuchElementException
   6: dup
   7: ldc #13; //String Bad
   9: invokespecial #17; //Method java/util/NoSuchElementException."<init>":(Ljava/lang/String;)V
   12: athrow
  Exception table:
   from   to  target type
     0     1     2   Class java/lang/ClassCastException

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      13      0    this       LC;
   0      13      1    x       Ljava/lang/Object;

  LineNumberTable:
   line 8: 0

  StackMapTable: number_of_entries = 1
   frame_type = 66 /* same_locals_1_stack_item */
     stack = [ class java/lang/ClassCastException ]

  Signature: length = 0x2
   00 26

public C();
  Code:
   Stack=1, Locals=1, Args_size=1
   0: aload_0
   1: invokespecial #24; //Method java/lang/Object."<init>":()V
   4: return
  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      5      0    this       LC;

  LineNumberTable:
   line 7: 0


}

Would it not be an improvement to implement get as a macro which expanded, based on T,
to either an asInstanceOf in a try-catch (ClassCastException => new NoSuchElementException)
or an isDefined-based implementation for the (important) edge case where T is castable from the type of
NoneValue? It would slow down the case of calling get on an empty Option, but at the gain of having almost
no overhead in 'correct' get usage (where you already checked isDefined).

杨博

unread,
May 3, 2013, 10:39:35 AM5/3/13
to scala-l...@googlegroups.com
I think the problem is that 
The value class specification is very incomplete, missing many important features, and impose many unnecessary limit:
  1. Scala compiler should allow a value class has zero parameter.
  2. Scala compiler should allow sealed value trait, e.g. sealed trait T extends AnyVal
A hint to implement sealed value trait:
  • When declaring a sealed value trait, the Scala compiler should generate a box method in the trait's companion object.
  • When declaring a val with sealed value trait type, Scala compiler should generate a field with java.lang.Object type in underlying byte code.
  • When invoking any method on a sealed value trait, the Scala compiler should box the underlying java.lang.Object to the trait type, before the wanted call.
For example:

/*
// Should generate this companion object:
object ValueTrait {
  def box(value:Any) = value match {
    case int: Int => new IntValue(int)
    case string: String => new StringValue(string)
  }
}
*/
sealed trait ValueTrait extends AnyVal {
  def printMe()
}

final class IntValue(int: Int) extends AnyVal with ValueTrait {
  override def printMe() {
    println(int)
  }
}
final class StringValue(string: String) extends AnyVal with ValueTrait {
  override def printMe() {
    println(string)
  }
}
object Main {
  def main(args: Array[String]) {
    /*
      // Should be transformed to
      val v:Any = 0
    */
    val v:ValueTrait = new IntValue(0)

    /*
      // Should be transformed to
      ValueTrait.box(v).printMe()
    */
    v.printMe()
  }
}

When scalac support sealed value trait and zero parameter value class, just change scala.Option's base class to AnyVal, and all optimization is done, without breaking any source-level compatibility.

Scott Carey

unread,
May 4, 2013, 4:01:49 PM5/4/13
to scala-l...@googlegroups.com
Think about it this way -- at best an equals is comparing two pointers.  Instanceof can do better.  It knows at compile time what the type is, the JVM can push the value of the class reference into the code as a constant -- so now it is comparing a value on a class (the value of the class pointer) to a constant (or list of constants for a type hierarchy).  This is the same reason why null checks are faster than equals -- one side of the check is a constant.

Scott Carey

unread,
May 4, 2013, 4:29:58 PM5/4/13
to scala-l...@googlegroups.com


On Wednesday, April 24, 2013 3:18:49 PM UTC-7, rklaehn wrote:
Hi all,

I think it is clear that if you want to replace scala.Option with a value class, you have to support Some(null). There is just no way around it even if it might be ugly.

My thoughts are to have a class that is Option-like that does not have Some(null), since that is the heart of the issue for use as a tool for dealing with null with full type safety.  Support of Some(null) means that Option is not null-safe.  I simply want a type that encodes whether the value can be null or not, and forces me to handle it appropriately.

As Paul has said a few times, the root of the problem is that Option conflates two things:  Is there a result?  Is the value null?   These are not the same thing.

I'll call the thing that only answers the question "Is the value null?" and therefore does not support Some(null)  "Opt" to be like a prior similar proposal.

The only constructor in this hypothetical Opt[A] is Opt(val: A).  If null is passed in it becomes a None, otherwise a Some.  Pattern matching with Some either would not be supported, or fail if null was passed in.  I'm thinking that Some could be hidden from the user entirely, but have not worked through it.  Opt(null) evaluates to None.  Opt(None) also evaluates to None (since it is null in disguise).  Opt(Opt(x)) also must equal Opt(x) for it to work as a value class and faithfully be merely "is it null?".  This causes some functor/monad issues.

The above feels do-able as a value class, since there is never any 'extra' information needed than whether the reference inside is null or not.  Support for Some(null) and Option(Option(x)) requires extra information or indirection at runtime.

Rüdiger Klaehn

unread,
May 5, 2013, 3:38:24 AM5/5/13
to scala-l...@googlegroups.com
On Sat, May 4, 2013 at 10:29 PM, Scott Carey <scott...@gmail.com> wrote:


On Wednesday, April 24, 2013 3:18:49 PM UTC-7, rklaehn wrote:
Hi all,

I think it is clear that if you want to replace scala.Option with a value class, you have to support Some(null). There is just no way around it even if it might be ugly.

My thoughts are to have a class that is Option-like that does not have Some(null), since that is the heart of the issue for use as a tool for dealing with null with full type safety.  Support of Some(null) means that Option is not null-safe.  I simply want a type that encodes whether the value can be null or not, and forces me to handle it appropriately.

Option is not a tool for dealing with null. Dealing with null is one of many applications of option, but there are many perfectly valid use cases for Option that deal with value that can not be null to begin with. But Option[T] must always have one more possible value than T (an Option[Boolean] has 3 possible values while Boolean has 2, etc. Taking out one perfectly valid value such as null and replacing it with None has nothing to do with option, but is just a convenience thing in the Option.apply method because java programs often use null to denote None.

An Option[T] is an optional value of T, so every possible value of T must be possible in Some[T]. Since null is a valid value for anything extending AnyRef, option has to deal with this.
 
As Paul has said a few times, the root of the problem is that Option conflates two things:  Is there a result?  Is the value null?   These are not the same thing.

I'll call the thing that only answers the question "Is the value null?" and therefore does not support Some(null)  "Opt" to be like a prior similar proposal.

The only constructor in this hypothetical Opt[A] is Opt(val: A).  If null is passed in it becomes a None, otherwise a Some.  Pattern matching with Some either would not be supported, or fail if null was passed in.  I'm thinking that Some could be hidden from the user entirely, but have not worked through it.  Opt(null) evaluates to None.  Opt(None) also evaluates to None (since it is null in disguise).  Opt(Opt(x)) also must equal Opt(x) for it to work as a value class and faithfully be merely "is it null?".  This causes some functor/monad issues.
 
Option[Option[T]] works just fine with the current implementation. The compiler knows the type and boxes it accordingly if necessary.

scala> val x : Any = Some(Some(3))
x: Any = Some(Some(3))

scala> x.getClass
res0: Class[_] = class scala2.Option

scala> x match { case Some(v) => println(v.getClass); case None => }
class scala2.Option

Reply all
Reply to author
Forward
0 new messages