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 =)
Pingback: Matching a combination of class and trait in Scala « JCranky's Blog!