Matching a range of numbers in Scala

Hi everybody!

Yesterday I did a presentation about Scala at Globalcode. It was very nice, thank you all who attended!

Now, one of the things I liked the most was the final part when people started making questions. I must admit I didn’t have answers to some of them, and this post is about one of those.

The question was something like: “can I pattern match against a range of numbers, like, say, 1 to 10?”

The answer is yes, although with a little more code than I thought necessary – yet, the solution is simple. First, if we want to match a simple number, we could write something like this:

myNumber match {
  case 10 => println("a ten")
  case num: Int => println("a number")
  case _ => println("something else")
}

In terms of matching numbers, we have two cases here. In one of them, we match directly a ’10’. In the other, we match any number. But how do we match a range of numbers? By adding something extra in the ‘any number’ case:

myNumber match {
  case 10 => println("a ten")
  case num if 0 until 100 contains num => println("a number between 0 and 100")
  case _ => println("something else")
}

This ‘something extra’ is called a ‘guard’. Guards allow us to add more verifications to values we match. With this, we can have any kind of restriction in any matched value. Also, we could have a ‘case’ for numbers between 0 and 100, and another one for any other number, like this:

myNumber match {
  case 10 => println("a ten")
  case num if 0 until 100 contains num => println("a number between 0 and 100")
  case num: Int => println("a number")
  case _ => println("something else")
}

Finally, please mind the order of the cases. Notice how we go from the more specific numbers, to the broader matching. This is not optional. you cannot put the broader case before the other ones – the code won’t even compile. In summary, this won’t work:

myNumber match {
  case num: Int => println("a number")
  case 10 => println("a ten")
  case num if 0 until 100 contains num => println("a number between 0 and 100")
  case _ => println("something else")
}

Now lets talks about how this work. Like a lot of things in Scala, ‘0 until 100’ is not a special construct. It is implemented as library – which means you could have implemented this yourself. The steps that the scala compiler has to take to make this magic happen is something similar to the following:

  • 0 is an Int, so the compiler tries to find an ‘until’ method in the Int class, but it doesn’t exist;
  • the compiler finds out that the ‘until’ method exists in the ‘RichInt’ class;
  • it would be perfect if 0 was a RichInt… so the compiler now searches for a way to transform an Int in a RichInt;
  • luckily, it finds this: ‘implicit def intWrapper (x: Int) : RichInt’ in the Predef object;
  • 0 is then wrapped in a RichInt, and the compiler calls its until method, with ‘100’ as the parameter;
  • the ‘until’ call results in a Range object and the compiler calls its ‘contains’ method, passing ‘num’ as the parameter;
  • the result of ‘contains’ is a boolean, which will decide if the number matches or not which what we want.

That’s quite a lot of stuff happening, even though there is nothing too complex in there. To close, one more last bit of information. Predef is an object that contains a lot of helper implicit methods, which are in the classpath of any scala application by default. Very handy.

I would like to thank Henrique Prage, who was in the Scala presentation, and sent me this link this morning: http://stackoverflow.com/questions/1346127/can-a-range-be-matched-in-scala – it really helped me find out quickly the answer to this question =)

Advertisements
This entry was posted in scala and tagged , , , , , , . Bookmark the permalink.

One Response to Matching a range of numbers in Scala

  1. Pingback: Matching a combination of class and trait in Scala « JCranky's Blog!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s