Pattern matching in Scala 2.10

One of the improvements that came with Scala 2.10 is completely rewritten and improved pattern matching. In addition to fixing some performance issues (e.g. this exponential space bytecode bug), it fixes a few of the annoyances I’ve had with pattern matching in Scala and gives much more useful errors at compile time.

Better matching on sequences

Now we can match on Seq more like we match on lists using +: and :+. For example:

def sort[T <% Ordered[T]](seq: Seq[T]): Seq[T] = seq match {
  case Seq() => Seq()
  case x +: xs =>
    val (before, after) = xs partition (_ < x)
    sort(before) ++ sort(x +: after)
}
sort(IndexedSeq("Greg", "Eishay", "Andrew")) // => List(Andrew, Eishay, Greg)

Since it’s preferred to use the Seq type over List in most places, this should probably be preferred over :: when possible.

Note that xs :+ x captures the last element of the list. You should generally only do this with an IndexedSeq or some other collection that can get the last element efficiently.

Compile-time checking of type arguments

In 2.9, type arguments would not be checked when destructuring many types. For example, the following code compiles in 2.9:

val (a, b): (Int, Int) = ("a", "b")
val List(a, b, c): List[Int] = Some("hello")

The compiler would issue a warning, and then we’d get a ClassCastException when running the code:

val (a, b): (Int, Int) = ("a", "b")
:7: warning: non variable type-argument Int in type pattern (Int, Int) is unchecked since it is eliminated by erasure
       val (a, b): (Int, Int) = ("a", "b")
                   ^
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
...

In 2.10, the type checker is smarter, and we get a nice message at compile time:

scala> val (x, y): (Int, Int) = ("a", "b")
:7: error: type mismatch;
 found   : String("a")
 required: Int
       val (x, y): (Int, Int) = ("a", "b")

Abstract types

We can also now match on abstract types. So we can do stuff like:

scala> import reflect.ClassTag
import reflect.ClassTag
scala> def filterBy[T: ClassTag](seq: Seq[Any]) = seq collect { case x: T => x }
filterBy: [T](seq: Seq[Any])(implicit evidence$1: scala.reflect.ClassTag[T])Seq[T]
scala> filterBy[String](Seq(1, "two", "three"))
res0: Seq[String] = List(two, three)

Note that, unfortunately, we can’t match value types like Int when they are boxed (we have to match the Java wrapper type).

We wrote this post while working on Kifi — tools that help people outsmart information overload together. Learn more.

2 comments
mslinn
mslinn

The colors and font for the code examples are hard to read :(

vertexshader
vertexshader

I love this post.  More posts about new scala improvements please!

Trackbacks

  1. [...] One of the improvements that came with Scala 2.10 is completely rewritten and improved pattern matching. In addition to fixing some performance issues (e.g.this exponential space bytecode bug), it fixes a few of the annoyances I’ve had with pattern matching in Scala and gives much more useful errors at compile time.  [...]

  2. [...] Andrew Conner wrote a short post on what’s new with Scala 2.10 pattern matching [...]

  3. […] due to Scala’s major-release binary incompatibility and pattern match failures prior to the 2.10 rewrite. While these are indeed frustrating, I’ve become a better programmer through troubleshooting […]