Konrad is passionate about the JVM, and the whole ecosystem that surrounds it. Lately he fell in love with Scala, but that doesn't mean he's not into Java or dynamic languages. Other than that, he?s a fan of automating every possible task and ridiculously long keyboard shortcuts. "After hours" he's still bound to programming - as a lead of the PolishJUG, the lead of the Google Developers Group Kraków and helping hand of Software Craftsmanship Kraków he's coding for fun and glory, speaking at conferences, or organising meetups ranging from small hackathons to big conferences like the annual GeeCON. In those rare times when he's not doing something code-related, he's collecting game consoles or playing tennis / squash. He bloggs and tweets. Konrad is a DZone MVB and is not an employee of DZone and has posted 12 posts at DZone. You can read more from them at their website. View Full User Profile

Scala 2.10 – or, Why You Will Love Implicit Value Classes

12.29.2012
| 6395 views |
  • submit to reddit

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:Scala Types

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:

class Meter(val n: Int) extends AnyVal

def traveled(m: Meter) =

s"You traveled ${m.n} meters!"

val meters = new Meter(34)

traveled(meters) should equal ("You traveled 20 meters!")

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):

// def traveled(m: Meter) was compiled as:

public java.lang.String traveled(int); // where's my Meter?!

 

// val meters = new Meter(34)

0: bipush 34 // ok that's the int...

2: istore_1 // that's all, no Meter in sight!

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:

class Meter(val n: Int) extends AnyVal {

def traveled = s"You traveled ${n} meters!"

}

val meters = new Meter(34)

meters.traveled should equal ("You traveled 20 meters!")

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?

// [meters.traveled] was compiled into:

22: invokevirtual	#37; //Method Meter$.traveled$extension:(I)Ljava/lang/String;

// so it's calling a method on the companion object, which takes an [int]

// still no Meter in sight here...

 

// :javap -v Meter

// does Meter even exist, if I'd want to use it explicitly?

public java.lang.String traveled();

7: invokevirtual #26; //Method Meter$.traveled$extension:(I)Ljava/lang/String;

// yes it exists, and is delegating all the work to the companion object

// :javap -v Meter$ // the [companion object]!

public final java.lang.String traveled$extension(int);

// which contains the actual implementation of traveled

// notice the $extension suffix marking that it's a generated method.

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:

trait AwesomeStringImplicits {

// boilerplate...

implicit def asAwesomeString(s: String) =

new AwesomeString(s)

class AwesomeString(s: String) {

def awesome_! = s + "! Awesome!"

}

}

 

object Main extends App

with AwesomeStringImplicits {

"Scala".awesome_!

}

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:

implicit class AwesomeString(val s: String) {

def awesome_! = s + "! Awesome!"

}

// an [implicit final def AwesomeString(String): AwesomeString] is generated for us automatically

 

"Scala".awesome_!

 

// 3: ldc #21; //String Scala vvvvvvvvvvvvvvvvvvvvvvvvvv

// 5: invokevirtual #25; //Method AwesomeString:(Ljava/lang/String;)LTesting$AwesomeString; << returns boxed!

// 8: invokevirtual #30; //Method AwesomeString.awesome_$bang:()Ljava/lang/String;

// Called: AwesomeString.awesome_$bang:()Ljava/lang/String;

// This impl. takes the `s` from the AwesomeString's instance!

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:

implicit class AwesomeString2(val s: String) extends AnyVal {

def awesome_! = s + "! Awesome!"

}

 

"Scala".awesome_!

 

// 18: ldc #35; //String Scala vvvvvvvvvvvvvvvvvvvvv

// 20: invokevirtual #39; //Method AwesomeString2:(Ljava/lang/String;)Ljava/lang/String; << returns not boxed!

// 23: invokevirtual #42; //Method AwesomeString2$.awesome_$bang$extension:(Ljava/lang/String;)Ljava/lang/String;

// vvvvvvvvvvvvvvvvvvv

// Called: AwesomeString2.awesome_$bang:(Ljava/lang/String;)Ljava/lang/String;

// as you can see the method gets the string passed in right away

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?

Published at DZone with permission of Konrad Malawski, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Krish Sinha replied on Thu, 2013/09/26 - 4:53am

Very good information.I like it.I have also found one really good information about scala  from guruzon.com 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.