Scala 2.10 – or, why you will love implicit value classes

Posted by Konrad 'ktoso' Malawski on 23/12/2012 – 00:31;

Hip hip hurray. Scala 2.10 is Final (as can be read on scala-internals) so it’s high time for another blog post celebrating all the awesomeness it’s going to bring into our daily lifes!

This time we’ll cover a pretty awesome SIP, namely: SIP-15 Value Classes. If you’re not familiar at all with the idea you should either read the SIP now, or stay with me learn why they’re so awesome, and then read the SIP. I’ll give you a paragraph of dots to think about it.

Seems you’re back! Let’s get hacking then.

So a Value Class is a smart way of encapsulating a number (for example) with a class, to make it efficient to use such simple wrapper classes. Let’s look into the “Wins” of Value Classes in two examples

Win 1: Better type checking, near-none performance costs

Before we start implementing stuff, a quick reminder about Scala’s Type Hierarchy:

skitch (5)

Focus on the Any class and it’s two children for now. On the right, we have AnyRef, which is Java’s Object, and all our classes that we write in Scala (or Java) extend it. Scala implementations have the additional layer of ScalaObject, but that’s it – this is the side of the type tree where our objects live.

With Value Classes this changes…

as we’ve become able to write classes which are on the AnyVal side of this hierarchy! This isn’t exactly a new feature for the compiler, as you can see that’s how we’ve been using java “int” which was decorated as “scala.Int” for years now. What changed in 2.10 is the fact that we can now add our own classes on this branch of the tree!

Let’s just see how Value Classes work in this blogpost, and I’ll post a more concrete example in another post, as this one is getting pretty long :-) The first question we need to answer is – “Why being on the left side here is such a big deal?”. Let’s start out with just a Meter (as in Distance) class:

Seems pretty normal. The only difference, which made it a Value Class, is the fact that we explicitly extend AnyVal. Now is a good moment to take a look at the generated bytecode (just type the example in an object Test in the REPL and the run javap -v Test to see for yourself):

Now you should probably “get” what the Value class changed… Yes, the class Meter is not used in the final bytecode! Instead we’re operating on a plain value – the Int parameter that Meter was wrapping. Thus, we didn’t add any memory overhead connected to introducing this class!

Ok, but what if this class will have some methods?

Let’s move the traveled method inside the Meter class and find out:

We’ve now moved the traveled method inside of our Meter class. What does this mean for the runtime – just an int won’t do, since we’re calling things on it… right?

As you can see – an int is plenty enough to support even method calls “on it”! The compiler is smart enough to replace all Meter.traveled calls with a static call to it’s companion object Meter$.traveled(int) which as you can see… again takes a plain int, instead of an Meter instance!

That’s pretty sweet – we the type safety of Meter at compile time, and we can use it to make our methods “feel strongly about” what is passed into them – accept only Meter, yet at runtime we don’t have ANY memory increase due to passing around big objects – we’re back to primitives!

Win 2: Implicit methods get a boost!

This “Win” could also be called “Implicits loose the runtime overhead of creating the Wrapper class each time!” or “Scala gets real Extension Methods!”.

Just a quick reminder on implicits in Scala: We can use implicit conversions to decorate some type, with another type and, seemingly, add methods to it. In Scala 2.9 we’d do it like this:

So there are two new things here in Scala 2.10, one is that we can get rid of the asAwesomeString method, which pretty much was just boilerplate to convert a String into an AwesomeString.

Implicit classes are also new in 2.10

This is done by using an implicit class (which are described in detail in SIP-13), but this is not our focus today. Just assume they generate the boilerplate called “asAwesomeString” for you, and work the same way otherwise.

What’s more interesting for us in this blog post here is that by using both features SIP-13 and SIP-15 we can construct an implicit value class. Let’s take a look at the bytecode generated by both, the implicit class as well as the implicit value class to spot the difference:

The implicit class goes first:

Most notably, take a look at the parts of the bytecode marked with ^^^^ and vvvv. The AwesomeString method (generated for us by the compiler) returned the Wrapper class here, and then on the instance of AwesomeString the .awesome_!() method gets called.Pretty normal implicits use so far. Bear in mind that this wrapper creation is done for each implicit method you call – it’s not cached, even if you chain multiple implicit calls on the same String instance that we just wrapped.

Implicit Value Class

In contrast, this is the bytecode generated for an implicit value class:

Let’s again focus on the bytecode marked with vvvv and ^^^^. This time the wrapper function is still called, but it returns the same string instance that came in (ScalaGurus – why can’t the compiler just skip this invocation and call the extension method right away?).

Then this string is passed right into the awesome_!(String) method defined on the companion object. As you see the difference here is that we pass in the unboxed value to the extension method (yeap, let’s call it this way) whereas before we would call this method on the wrapper object.

Turns out that using “plain extension methods”, via defining implicit value classes has noticeable performance benefits (small but hey, it adds up!). And if you would like to see this used somewhere else than just “some guys blog”, look no futher and check out StringOps in Scala’s sources.

More info @ ScalaWags

Since Daniel, Dick & Josh have just recenly discussed this (and other) features of Scala 2.10 I’d like to link their awesome ScalaWags podcast here. This is an exact link to the part where they discuss value classes as well as implicit classes, so feel free to grab a coffee and hear then out nerding around the Scala Collections library – something we all love to do :-) Oh and be sure to follow @TheScalaWags on twitter.

Shameless Plug: GeeCON 2013 – Call For Papers

A small shameless plug… As we’re right in the middle of preparing 2013′s “All around the JVM” GeeCON conference, I thought that bringing in even more awesome Scala people would be… even more awesome? ;-) So, if you’re interested in anything around the JVM (Scala? :-)) – why not submit your talk to our C4P?

Tags: , , , , , , ,

This post is under “coding, java, scala” and has 6 respond so far.

6 Responds so far- Add one»

  1. 1. Tom Said:

    Your gist links seem to be borked. All I can see are tags like [gist id="4360893" file="implicit_value_class.scala"]

  2. 2. Ktoso Said:

    Thanks for pointing it out! Fixed

  3. 3. Tomasz Bartczak Said:

    Nice explanation!

    I wonder why do I get an implicit boxing for an implicit value class, but I do not get an implicit unboxing?

    given I have:
    implicit class EmailValue(val email:String) extends AnyVal

    when I want to:
    val stringValue:String = new EmailValue(“unwrap me”)

    then I need to add an implicit conversion method:
    implicit def asString(s: EmailValue) = s.email

    but maybe I still miss something here.

  4. 4. Konrad 'ktoso' Malawski Said:

    Hello Bartek,
    This is not really about “implicit boxing / unboxing”, but about runtime representation. So a Value Class allows you to “think it’s an object”, and use it like you would use a normal object, while during runtime – that object may never have to be allocated!

    You can read more about this in my article “types of types” http://ktoso.github.io/scala-types-of-types/#value-class or drop in for a chat during http://lambdadays.org in Kraków this week if you’re from Kraków? :-)

  5. 5. Tomasz Bartczak Said:

    Yes, I got the point about runtime representation as well. But I am not that eager to read bytecode to verify this part ;-) I will take a look at the link, as Type System in Scala is really interesting, and it still has some dark parts (from my perspective)

    However I am still wondering why there is an implicit conversion in on direction and not the other?

    BTW – my name is Tomek, not Bartek. And unfortunately not from Kraków. But we have a ScaLa group in Warsaw as well ;-)

Post a reply