Tuesday, April 28, 2009

Scala multiple assignment

Maybe I am a bit slow, but I finally taken the ability to assign multiple values in a single assignment using tuples and extrapolated to the more general pattern matching case. The only way I can really explain is with examples:

Here is the basic case that is shown in so many examples:

scala> val (x,y) = (10, "ten")
x: Int = 10
y: java.lang.String = ten

In this example the Tuple (10,"ten") is assigned to x and y using the same mechanism that match and case uses.

scala> (10, "ten") match {
| case (x,y) => println(x,y)
| }

I finally came to the realization that if we can do this with Tuples can we not do it with other case classes or classes with Extractors?

scala> case class T(one:Int, two:String)
defined class T

scala> val t = T(6,"six")
t: T = T(6,six)

scala> val T(i,j) = t
i: Int = 6
j: String = six

scala> val T(a,b) = T(10,"book")
a: Int = 10
b: String = book

Some may ask: "Why do you need this if you can use Tuples?" My answer is that Tuples while handy have no semantic meaning. A case class can have meaning. So it is better to pass around an object with meaning rather than a Tuple.

Consider the case where a method returns a case class, one could use this method to extract just the values desired or extract them into local variables:

scala> val T(a,_) = T(10,"book")
a: Int = 10
Pretty cool eh!

Now in the spirit of "how deep down the rabbit hole can we go":

scala> object Names{
| def unapply(s:String): Option[String] = {
| if( s.trim.startsWith("Mr")) Some(s.trim.drop(2))
| else None
| }
| }
defined module Names

scala> val Names(name) = "Mr Jones"
name: String = Jones

Wow so extractors also work. This is too much fun :)


Jeremy Mawson said...

Oh, wow. This is quite awesome! I must commit this to memory for my bag of tricks. Thank you.

Dave Griffith said...

While wicked cool, this doesn't really count as multiple assignment, but rather as a lovely syntax for multiple destructuring declaration. You can't reassign variables this way, either for tuples or the general extractor case. This

var x = 3
var y = 5

(x, y) = (42, 43)

is invalid

Jesper said...

You write that tuples has no semantic meaning, I don't agree with this. The element types define the semantic meaning of a tuple. For example, you can emulate a case class by using the first tuple element to distinguish class:

scala> object A
defined module A

scala> (A, 1) == (A, 1)
res10: Boolean = true

scala> (A, 1) == (A, 2)
res11: Boolean = false

scala> object B
defined module B

scala> (A, 1) == (B, 1)
res12: Boolean = false