Saturday, June 11, 2011

Processing Scala Options

The Scala Option has really grown on me.  I can't believe I didn't embrace it earlier.  There are several great posts, but one of the best I've found is scala.Option Cheat Sheet from Tony Morris' blog. 

Yesterday, I was trying to unload an option, and was getting (what I thought) were strange results from .getOrElse.  This was all occurring inside an Akka actor, and if the option was empty (None), then I wanted to send the actor's reply message.  If there was a value (Some(str)), I wanted to assign the "x" to a value.  The code looked something like this:

val result = returnsAnOptionString.getOrElse(self.reply(None))

The problem with the above statement is that when Some(str) comes back, the type of result was Any when I was expecting String.

I asked Paul Snively about this, and he kindly pointed me to the signature of getOrElse which looks like this:

def
getOrElse [B >: A] (default: ⇒ B): B

My interpretation of this is that getOrElse returns something of type B where B has a lower bound of A, and takes a parameter named default that is defined as a "by-name" parameter of type B.  In this case, A is String, so B is any superclass of String. 

Since Akka's Actor.reply returns an Any, and Any is a superclass of String, getOrElse(self.reply(None)) returns an Any.

So, the hunt was on for a better way to get x as a String.  The most obvious way would be to tack .asInstanceOf[String] on the end, but that seems sort of ham-handed.  The next most obvious solution would be to use a match like:

val result = { returnsAnOptionString match {
   case Some(s:String) => s
   case None => self.reply(None)
}}
self.reply(result + "12345")

There is a similar problem with the above statement.  The val "result" still gets typed as Any.  So, in this case, it looks like I want to do the entire processing of the Some(String) inside the case and just skip the assignment.  Something like:

returnsAnOptionString match {
   case Some(s:String) => { val result = s + "12345"; self.reply(result) }
   case None => self.reply(None)
}

That will avoid the type issue with result, but I wanted to look for a better way.  Paul mentioned using a "for comprehension" to process only the Some(String) possibility and ignore the None possibility.  That could work something like this:

for ( s <- returnsAnOptionString ) self.reply(s + "12345")
self.reply(None)

That's pretty clean to my eyes, but Tony's blog post showed me that I could use foreach to simplify it a little bit more:

returnsAnOptionString.foreach(s => self.reply(s + "12345"))
self.reply(None)


Finally, Paul threw this one out for dealing with potentially null returns:

for { foo <- Option(someMethodThatCanReturnNull); bar <- somethingThatCantDealWitnNull(foo) } ...

So, I learned quite a bit about options this week.  Hopefully this helps someone!

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.