Make your own literals in Scala

November 14, 2009 📬 Get My Weekly Newsletter

Following up from my post on Deconstructing the map literal in Scala, I thought it might be fun to go the other way around, and create a few literals of own. Since Scala provides the language features to enable literal-like syntax, and not the actual literals themselves, we can do a lot of stuff to reduce the amount of typing we have to. In your production code, you probably wouldn't use a lot of literals, regardless of language support. However, in test cases, they are much more common. If creating objects in your tests becomes easier, you will tend to write better tests. Suppose we are writing a Twitter app (that's all the rage these days, anyway). We might define a TwitterUser class like so:
class TwitterUser(val username:String) {
  def url = "http://www.twitter.com/" + username
  def recentTweets = // imagine some code here 
}
We might write a few test cases:
val me = new TwitterUser("davetron5000")
assertEquals(10,me.recentTweets.size)

val fake = new TwitterUser("davetron5001")
assertEquals(0,fake.recentTweets)
and so forth. It's going to get a bit tedious writing new TwitterUser over and over again. We could just make a shorter-named method in our test class, but why not use the apply() sugar along with a singleton object to make our own literal?
// Along with our TwitterUser class def
object @@ { // "@" is a reserved word :(
  def apply(username:String) = new TwitterUser(username)
}

// back to our test code

val me = @@("davetron5000")
val fake = @@("davetron5001")
It's a small bit of code, and we've saved some typing without sacrificing readability, since we take advantage of the way Twitter refers to users via the "@" symbol. If our app is all about Twitter, we can go a step further and just rename TwitterUser to @@ like so:
// This replaces the TwitterUser class 
// and @@ singleton object
case class @@(val username:String) {
  def url = "http://www.twitter.com/" + username
}
This takes advantage of (the unfortunately named) case classes and now our Twitter user class and literal-like factory are called @@. This allows, among other things, some more readable case statements.
user match {
  case @@("davetron5000") => "it's you, dude"
  case user:@@ => "it's someone else"
}
Now, you certainly don't want to do this for every object in your domain (you'd get a horrible mess of symbols), but for key objects that you are using everywhere, an appropriately chosen literal syntax can make your code very clean and easy to read and modify.