//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:
Thanks, this getOrElse looked complicated in my Play example until I read your post :)
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.
Great example .. will be handy while using getOrElse method..
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.
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
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)
Good explanation!
Very nice example... Good Post!
Post a Comment