Tuesday, May 31, 2011

The magic of getOrElse

For the last month or so I've been coding in Scala. From time to time I use Option's; they seem pretty handy; it's much nicer to get an Option[T] back instead of a null. I semi-often find myself writing things like:

//where f is a function returning Option[Something]
   val t = f(args) getOrElse 3

Recently I realized (classic lightbulb moment :) that getOrElse actually takes a function as the argument:

def getOrElse [B >: A] (default: ⇒ B): B
  //Returns the option's value if the option is nonempty, otherwise return the result of evaluating default.

Why does this matter? It means that the code passed to getOrElse doesn't run at all (default is not evaluated) if the Option is defined! For example, this code:

object GetOrElseTest {
 def main(args : Array[String]) : Unit = {
  def a() = Some(3)
  def b() = None
  
  val aV = a getOrElse { println("nothing from a :("); 1 }
  val bV = b getOrElse { println("nothing from b :("); 1 }
  
  println("aV=%d, bV=%d".format(aV, bV)) 
  
  0
 }
}

Will print this output (note that the println for getting a, which is defined, never runs):

nothing from b :(
aV=3, bV=1

That's awesome and really hard to accomplish in Java. Imagine if log4j could do this; suddenly we wouldn't have any issues with things log.debug("a" + objectThatIsExpensiveToToString.toString()) having a runtime cost even when debug isn't enabled because we wouldn't evaluate the function to create a message at all if debug is off.

The new uniforms are pretty snappy eh first officer!

8 comments:

Magnus said...

Thanks, this getOrElse looked complicated in my Play example until I read your post :)

Bruce Richardson said...

Your example is a bit evil because it involves side effects. True, logging as a side effect is probably the least evil thing you could do and probably acceptable ;) Otherwise, I would prefer only to use the closure to construct a suitable alternative value, where that is not a simple matter or where it might be found in an alternative location.

Santhosh A said...

Great example .. will be handy while using getOrElse method..

Chris said...

Regarding: log.debug("a" + objectThatIsExpensiveToToString.toString()) having a runtime cost even when debug isn't enabled

You can use Logger.isDebugEnabled() to avoid creating Strings that don't get logged because of logger level. However, that applies just to the specific example above. getOrElse can be used for a lot more.

Milin said...

Hello

good article

relating your point on log4j,
in play framework they use call by name (: =>) to avoid immediate evaluation of the toString():

def info(message: => String) {
if (logger.isInfoEnabled) logger.info(message)
}

Francois Scheurer

Jukka Dahlbom said...

Your example with logging is bit flawed, since SLF4j already solves this by lazily evaluating the objects only when that debug level is enabled:
LOGGER.debug("This object {}.toString is not always evaluated", expensiveToStringable)

LRG said...

Good explanation!

Praveen Addepally said...

Very nice example... Good Post!

Post a Comment