Hacker News new | ask | show | jobs
by lmm 3722 days ago
Scala Some is a box (i.e. it takes up 8 bytes on top of whatever's in it). It would be nice to represent Some(x) as just the value x (at the Java bytecode level) and None as null. The problem with that is that if you have an Option[Option[A]] you can't tell the difference between None and Some(None) (which might be semantically important), and whatever you do to solve this your Option is no longer generic and behaves in different ways depending on what's inside it, which is a disaster for being able to understand and reason about code.

Kotlin treats nullability as an ad-hoc special case. This allows them to save those 8 bytes occasionally, but it means you simply can't handle optionality generically (in Scala you can write methods that will work generically with Option and also with other cases, e.g. "sequence" does the same thing when you call it on a List[Option[Int]] or a List[Future[Int]] or a List[Writer[String, Int]] or... and it all makes sense, because Option is just another plain old type in the language). IMO that's the wrong tradeoff for almost all use cases, but evidently the Kotlin folk disagree.

1 comments

Unfortunately boxes are not so efficient :(

A box takes at least:

• 4 bytes to point to it, unless you need a big heap and OOP compression isn't usable, in which case it's 8 just on the pointer.

• Either 4 or 8 bytes of mark word, depending again on 32 vs 64 bit mode.

• Either 4 or 8 bytes of class pointer, ditto.

• Then another 4 or 8 bytes for the pointer inside the box.

• GC algorithms impose some additional overhead too.

That's hoping there's no alignment padding going on.

If the JVM supported real value types, then a lot of this overhead would boil away, but not always all of it.

When you use Option[] as a local variable or as a return type of a method, then HotSpot can sometimes optimise it out using escape analysis. Unfortunately the escape analysis in the C2 compiler is quite conservative and can often fail. The Graal compiler has a different design for its escape analysis and can remove the overhead a lot more often. Graal does show better speedups on Scala code than on Java code:

http://lampwww.epfl.ch/~hmiller/scala2013/resources/pdfs/pap...

(old paper)

The ability to nest optionalities is not something I've wanted to do so far, whereas the ability to freely represent optionality without having to worry about cost is quite freeing. I think Kotlin has the right tradeoff here, especially as in the rare cases where you do want the ability to represent Optional<Optional<T>> you can just use the Optional class from the Java 8 standard library to do so.

I very rarely want to explicitly use an Optional<Optional<T>>. But every day I write code that works with a generic Option[A] and I want to not have to worry about what happens if someone calls it with A=Option[B]. I also frequently use Option as a parameter to some other datastructure like Kleisli or WriterT, which only works because it's an ordinary datatype that behaves like any other.

You say "you can just use the Optional class from the Java 8 standard library", but as far as I can see porting code that was written to use nullables would be a pretty big refactor, and there's no way at all to write generic code that works with both, so you'd end up having to maintain two parallel copies of your common functions.

Efforts to improve the performance of Option are of course to be applauded, but for a lot of use cases we're talking about the difference between (say) 50x faster than Ruby and 40x faster than Ruby. Scala is more than fast enough for a huge range of use cases; I don't think I've ever seen someone port code off Scala because the runtime performance wasn't good enough.