Skip to content
spacer

Scala for Java Refugees Part 4: Pattern Matching and Exceptions

28
Jan
2008

So far, we’ve been examining the similarities between Scala and Java.  Areas where Scala syntax is so similar to Java’s as to be almost identical.  Of course, this doesn’t take full advantage of the language, but it allows developers to utilize the cleaner Scala syntax within the restrictions of a familiar environment.  From this point on, things are going to get a bit weird.

A number of the “more advanced” features in the Scala language are clear departures from Java.  They may mirror the corresponding Java feature, but in syntax the concepts are totally different.  Among these “different features” is the concept of pattern matching, something critical to the understanding of exceptions.

Pattern Matching

As is oft parroted on the blogosphere, pattern matching in Scala is really a lot like Java’s switch/case construct.  So in Java, one might write something like this:

public boolean checkPrime(int number) {
    // checks if a number between 1 and 10 is prime
    switch (number) {
        case 1: return true;
        case 2: return true;
        case 3: return true;
        case 5: return true;
        case 7: return true;
 
        default: return false;
    }
}

It’s obviously an extremely naive prime number sieve (if you can call it that), but it demonstrates the example quite effectively.  This familiar usage of switch/case is basically just a shortened if/else block.  One could just as easily have written this as a series of if statements, however it would have been less concise.  Interestingly enough, switch/case is often discouraged in object-oriented languages in favor of cleaner architectures making use of polymorphism for the same result.  But I digress…

One of the major limitations of switch/case in Java (and really any C derivative language) is that it can only be used on primitives.  You can’t use switch/case to test a String, for example (a need which arises more often than one would think).  In fact, the most complex type testable within switch/case is the Enum, and even this is just being reduced to its ordinal values under the surface.  In short, switch/case is a very crippled hold-over from the dark ages of #include and extern.

Fortunately, the designers of Scala chose not to include this “feature” in their new language.  Instead, they implemented a “new” (it’s actually been around in other languages for a long time) concept called pattern matching.  At a basic level, it allows algorithms which are very similar to the checkPrime(int) example:

def checkPrime(number:Int):Boolean = {
  number match {
    case 1 => return true
    case 2 => return true
    case 3 => return true
    case 5 => return true
    case 7 => return true
 
    case _ => return false
  }
}

Looks a lot like the Java example, right?  The biggest difference which likely jumps out at you is the lack of a default statement.  Instead, we see the return of Scala’s ubiquitous wildcard character, the underscore.  Literally read, this example means: match the value within number; in the case that it is 1, return true; in the case that it is 2, return true; …; for any previously unmatched value, return false. 

Scala case statements can’t “overflow” into each-other (causing multiple matches) like Java’s can, so even if we weren’t returning values, the algorithm would still be safe.  In fact the method can be more concisely expressed as follows:

def checkPrime(number:Int) = number match {
  case 1 => true
  case 2 => true
  case 3 => true
  case 5 => true
  case 7 => true
 
  case _ => false
}

This works because the match statement actually returns whatever value is returned from the matched case statement.  This allows match/case algorithms to be far more “functional” in nature, actually improving readability (as in the above example).

Now if this were the end of Scala’s pattern matching capabilities, it would still be worth using.  However, Scala’s capabilities go far beyond mere integer comparison.  For example, Scala’s match statements are not restricted to “primitive” types (which Scala doesn’t have, incidentally).  You can just as easily perform pattern matching on strings or even more complex values.  For example, the instanceof operation in Scala looks like this:

var obj = performOperation()
var cast:Color = obj match {
  case x:Color => x
  case _ => null
}

In Java of course we would do something like this:

Object obj = performOperation();
Color cast = null;
if (obj instanceof Color) {
    cast = (Color) obj;
}

The Scala example may look a bit odd, but trust me when I say that it’s really quite elegant.  In the example, we’re actually performing pattern matching against the type of the input.  If the type matches, then the case-local variable x is populated with the value from obj and assigned type Color.  This variable can then be returned from the match, and thus assigned to cast.  If no type is matched, just return null.  Scala allows a great deal of power in its pattern matching in that it allows the implicit assignment of case-local variables based on the matches.  In the example given, x is implicitly assigned iff (if and only if) the matched value is of the given type.

Case Classes

Now type matching may be cool, but once again it fails to encompass the awesome power afforded by Scala’s match statement.  Not only is Scala capable of inspecting the type it is matching, but also values within that type.  This probably doesn’t make much sense, so once again we resort to code samples:

case class Number(value:Int)
 
def checkPrime(n:Number) = n match {
  case Number(1) => true
  case Number(2) => true
  case Number(3) => true
  case Number(5) => true
  case Number(7) => true
  case Number(_) => false
}
 
checkPrime(Number(12))

The first statement is the key to the whole thing: defining a new case class “Number” with a single property.  Case classes are a special type of class in Scala which can be matched directly by pattern matching.  The also have some other attributes such as a compiler-defined toString(), a proper equivalency operation and so on.  You can also create a new instance of a case class without the new operator (similar syntax to instantiation in C++).  But in every other respect, case classes can be treated identically to normal classes. 

Scalists (is that a word?) like to use case classes in situations where they need a “quick and dirty” class, due to the predefined operations and the conciseness of its instantiation syntax.  I personally don’t care for this convention, mainly because case classes have some odd corners which can bite you when you least expect.  For one thing, case classes cannot extend other case classes (though they can extend normal classes and normal classes can inherit from case classes).  More importantly, case classes become implicitly abstract if they inherit an abstract member which is not implemented.  This can lead to some very strange looking compiler errors when attempting pattern matching on what you thought was a valid hierarchy.

Anyway, back to our example.  For each case, we’re actually creating a new instance of Number, each with a different value.  This is where the significance of the case class instantiation syntax comes into play.  Scala then takes our new instance and compares it with the one being matched (and this is all type-checked by the way).  Scala sees that the instances are the same type, so it introspects the two instances and compares the property values (in this case, just value).  Now this would seem to be massively inefficient, but Scala is able to do some clever things with case classes in pattern matching and everything turns out nicely.

Everything seems sort of intuitive until we reach the final case, which is using our friend the underscore.  Of course we could have just written this as the “any case” (case _) but I wanted to demonstrate wildcards in case classes.  This statement basically means “match objects of type Number with any value”.  This is the case which is matched by our checkPrime(Number(12)) invocation farther down.  Oh, and as a side note, if null is passed to this function, Scala will throw a MatchError, proving once again the loveliness of the Scala type system.

Of course, this sort of matching doesn’t seem very useful.  All we did was encapsulate an Int within a case class.  While this is cool for illustrative purposes, it would be grounds for execution if seen in a real code base.  This is where the power of inheritence meets case classes.

class Color(val red:Int, val green:Int, val blue:Int)
 
case class Red(r:Int) extends Color(r, 0, 0)
case class Green(g:Int) extends Color(0, g, 0)
case class Blue(b:Int) extends Color(0, 0, b)
 
def printColor(c:Color) = c match {
  case Red(v) => println("Red: " + v)
  case Green(v) => println("Green: " + v)
  case Blue(v) => println("Blue: " + v)
 
  case col:Color => {
    print("R: " + col.red + ", ")
    print("G: " + col.green + ", ")
    println("B: " + col.blue)
  }
 
  case null => println("Invalid color")
}
 
printColor(Red(100))
printColor(Blue(220))
 
printColor(new Color(100, 200, 50))
printColor(null)

The output will look something like this:

Red: 100
Blue: 220
R: 100, G: 200, B: 50
Invalid color

There are a couple of important things about this example.  Firstly, you should notice that we’re heavily using the feature in pattern matching which allows us to assign new values as part of the match.  In each of the specific color cases (Red, Green, Blue) we’re passing an undefined variable v to the new case class instance.  This variable will be assigned the property value of the class if a match is made.  So for our first invocation, the matcher finds that we’re looking at an instance of Red.  It then retrieves the property value from the instance (red) and assigns that value to the local case parameter x.  This value is then usable within the definition of the case.

The second thing which should jump out at you is the use of polymorphic case classes.  Here we have several specific types of Color which each take a single value as their property.  This value is then passed to the super-constructor (along with a number of literal values, depending on the color).  Red, Green and Blue are all case classes, Color is not.  We can’t just say case Color(r, g, b) => because Color is just an ordinary class, it cannot be matched in such a fashion.

This pattern is also the first we have seen with a multi-line case.  Technically, this is still just a single expression (a scope) which itself contains multiple expressions, but you can still think of it like a switch statement with multiple statements prior to a break.

Finally, if nothing else applies, the instance will match case null and print our “Invalid color” message.  You’ll notice we did no explicit null checking, we just let the matcher handle the ugly details for us.  Now isn’t that clean?

Case classes are about the closest thing Scala has to Java’s enumerations.  Oh Scala does have a type Enumeration that you can do interesting things with, but idiomatically case classes are almost exclusively used in situations where enumerated values would be employed in Java.  This of course lends itself to greater flexibility (because case classes may contain values) and better “wow factor” when showing off your code.  spacer

Exception Handling

Well I’ve been promising all along that I would somehow tie this in with exceptions and the time is now.  It turns out that Scala doesn’t have exception handling, at least not in the way that Java does.  Instead, Scala allows you to try/catch any exception in a single block and then perform pattern matching against it.

Let’s take our checkPrime() example.  It really only handles integers between 1 and 10, so it would be quite nice to test this precondition and throw an exception if it fails.  We can do this quite trivially:

def checkPrime(n:Int) = {
  if (n < 1 || n > 10) {
    throw new IllegalArgumentException("Only values between 1 and 10")
  }
 
  n match {
    case 1 => true
    case 2 => true
    // ...
  }
}

Not very interesting from a theoretical standpoint, but it’s a solid piece of code.  You can see that Scala’s exception throwing syntax is almost identical to Java’s.  In fact, the only real difference between Scala’s exception dispatch and Java’s is that Scala does not have checked exceptions.  It’s not very apparent in this example, but we could have just as easily thrown an instance of java.lang.Exception or some other “checked” exception, without modifying our method signature.  Scala also does not force you to catch exceptions, it trusts that you’ll clue in when your program crashes.

So how do we invoke this method, pedantically watching for exceptions?  It turns out that the syntax looks surprisingly like pattern matching:

try {
  checkPrime(12)
} catch {
  case e:IllegalArgumentException => e.printStackTrace()
}

You see the catch clause is actually a match statement under the surface on any exception thrown by the body of try.  The catch runs through and matches against the various cases and finds our IllegalArgumentException rule.  Once again, we’re implicitly creating a new local variable within a pattern.

If we wanted to catch all exception types, we could resort to our old friend the underscore:

try {
  checkPrime(12)
} catch {
  case _ => println("Caught an exception!")
}

This syntax seems to really only lend itself to a single catch block, rather than Java’s concept of a different catch for each exception type.  But since catch is really just a pattern matching block, it seems obvious that we don’t really need more than one catch block, we can just handle different exception types as different cases:

import java.sql._
import java.net._
 
Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:testdb", "sa", "")
try {
  PreparedStatement stmt = conn.prepareStatement("INSERT INTO urls (url) VALUES (?)")
  stmt.setObject(1, new URL("www.codecommit.com"))
 
  stmt.executeUpdate()
  stmt.close()
} catch {
  case e:SQLException => println("Database error")
  case e:MalformedURLException => println("Bad URL")
 
  case e => {
    println("Some other exception type:")
    e.printStackTrace()
  }
} finally {
  conn.close()
}

Since Scala doesn’t actually have checked exceptions, we really could get away with not catching the MalformedURLException (since we know that it’s correct).  However, it’s worth showing the multiple-exception scenario just for the sake of example.

This example also shows use of the finally block, something which should be quite familiar to any Java developers.  In Scala, the finally works precisely the way it does in Java, so there should be no concerns about odd behavior when designing code which requires it.

Conclusion

Scala exceptions are fairly intuitive beasts.  They behave almost exactly like Java’s (with the exception of being all unchecked), providing a familiarity and an interoperability with existing Java libraries.  The exception handling mechanism however demonstrates considerably more power, making use of Scala’s built-in pattern matching capabilities and thus maintaining a far more internally consistent syntax.

The next article in our series will delve a little deeper into the hidden mysteries of Scala’s type system: traits and type parameters.

Comments

  1. Hi,

    “For one thing, case classes cannot extend other case classes (though they can extend normal classes and normal classes can inherit from case classes).”

    A recent commit from svn:

    “r13809 | odersky | 2008-01-25 15:33:18 +0000 (Fri, 25 Jan 2008) | 1 line

    case classes can now inherit from case classes.

    Seems like that restriction will be lifted in the next release.

    Ismael Juma Monday, January 28, 2008 at 2:16 am
  2. Hi,
    “It turns out that Scala doesnโ€™t have exception handling, at least not in the way that Java does.”
    This sentence sounds very weird to me! Whereas exceptions are handled somewhat differently in Scala than in Java, I find no way to see it in a way that could be described as “[it] doesn’t have exception handling”… The way you put it, sounds like it could be seen that way – however there’s try, catch and finally! BTW this is just a friendly observation and maybe I didn’t get your meaning spacer
    Keep up the series, I’m reading them all.

    German B. Monday, January 28, 2008 at 6:57 am
  3. Neat! If only we had this sort of pattern matching in Java instead of the proposed multiple-exception catch declaration… Looks like yet another place were Scala solves a current issue in Java by providing powerful underlying flexibility instead of just adding syntax sugar for the superficial problem…

    Sun, please adopt Scala as Java 3. spacer

    Kieron Wilkinson Monday, January 28, 2008 at 9:34 am
  4. I have to say I’m not completely sold on the lack of checked exceptions. I wish the Scala guys went into more details about why they decided to do away with them in their FAQ. I imagine they make the whole functional aspect far simpler, and maybe Scala would be impossible to implement as it is with checked exceptions. On that balance, I would be happy to do without them I suppose. However, just like static type checking, they make me feel more confident in code.

    When I am working with a large code base, with complicated code execution paths, using checked exceptions (appropriately, of course spacer ) lets me more clearly see the paths of failure at compile time. In some places, I have even added checked exceptions were they were not appropriate (shock) and then reverted to unchecked – just to to get the compiler to do some path checking for me. Having said that, that system was developed without the benefit of TDD, and it is now very hard to add. Perhaps in a more TDD-fluent system it is not so much of a problem…

    I’m trying to keep an open mind though. Scala is a completely different language after all.

    Kieron Wilkinson Monday, January 28, 2008 at 9:47 am
  5. A typo in the discussion of constructor argument extraction from the Color classes: the variable in the example code is “v” but in the text discussing it you use “x”.

    @Kieron: checked exceptions are extremely painful when working with high-order functions. While Daniel is intentionally sticking with the imperative side of Scala here, the language designers are very oriented toward functional programming in general and high-order functional programming in particular.

    A simple example: take the “List.foreach()” method. This method has a single argument which is a closure that will be executed for each item in the list. If the closure ever throws an exception, that exception will simply propagate out of the foreach() call. The List class can’t possibly know all of the exceptions that all of the closures ever passed to it could throw, so there’s no way to list the checked exceptions on the signature of List.foreach(). The only way that this can work is to have all exceptions thrown by the closure be unchecked.

    Doug Pardee Monday, January 28, 2008 at 10:37 am
  6. @Doug
    Typo fixed. Thanks for catching this!

    @Kieron
    There’s a lot of discussion around the water-cooler these days about whether or not checked exceptions are a bad idea. Theoretically speaking, they don’t really add much to the language other than enforcing some degree of documentation and a whole bunch of annoying syntax whenever a checked exception-throwing method must be invoked.

    Checked exceptions don’t really relate to static type checking. While exception *instances* are themselves types, specified thrown exceptions are not. Exceptions are technically highly-imperative control structures which allow an inversion of the call stack. Scala is quite statically typed, I can assure you. spacer Simple example:

    abstract class Color
    case class Red extends Color
    case class Blue extends Color

    val test = Red()
    test match {
    case Red => println(“red”)
    case Blue => println(“blue”)
    case _ => println(“color”)
    }

    This code will fail with a compile error at the second case statement. This is because the inferred type of “test” is Red. Since Blue is not in Red’s inheritance tree, it is not a valid pattern to use in the match. A simpler example would be:

    var a = “12345″
    a = 12345 // compiler error

    Daniel Spiewak Monday, January 28, 2008 at 1:24 pm
  7. 1 is not a prime number spacer

    Michel S. Monday, January 28, 2008 at 5:20 pm
  8. It really depends on how you define it. If you say that a prime is any integer evenly divisible only by itself and 1, then 1 is in fact a prime, though a special case.

    Daniel Spiewak Monday, January 28, 2008 at 6:27 pm
  9. @Doug
    Thanks for that, I did wonder whether that was the case, but couldn’t quite visualise it. I makes sense to me now. I guess thinking functionally is the best argument I have seen for not using them.

    @Daniel
    Well, I would argue that they add a little bit to the language other than documentation or annoying syntax. spacer As I mentioned before, I like how they give some compiler checking to correct error propogation through code, which has certainly helped me dealing with complicated call-stacks like some of the code base here. However, I do agree this is a minor advantage, and as Doug illustrated, gets in the way of functional programming enough to sacrifice them.

    Hmm… I wonder if there is another way to somehow visualise that sort of thing…

    Thanks for the note about static type checking, but just to clarify, my example of static typing was purely an analogy made in respect to having confidence in code. The fact that Scala’s is strongly type checked (and seems to do it better than Java) is the only reason I

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.