Consolidated - originally posted on 2010/03.
Having options is cool...even scala's Option[A]
... :)
Let's take a closer look at it, the functional/monadic aspect of it and see how we can use it to inspire new patterns and new ways of looking at the code we write.
One way to use Option is plain:
addressMap.get("John") match {
case Some(addr) => popupMap (addr)
case None => logError(...)
}
or "java style" (synonym with "plain ugly"):
x = addressMap.get("John").getOrElse (null)
if (x != null) {
popupMap (x)
} else {
logError(...)
}
As you use scala more, you understand why monads are elephants and start doing it nice:
addressMap.foreach (popupMap(_))
addressMap.map (_.city)
And then you start to understand it as a pattern, basically you understand there's a range of problems were options apply. Say you create a small scripting framework (the same applies to say a command pattern framework). A script's execution may return successfully and with a value, or give a syntax error or a bunch of other options.
/** the result of running a smart script - default is like None */
class RSResult {
def foreach (f:Any=>Unit) {}
def map (f:Any=>Any) : RSResult = RSUnsupported
def getOrElse (f: => Any) :Any = f
def jgetOrElse (f:Any) :Any = getOrElse(f)
}
/** script was successful - have a result to work with */
case class RSSucc (res:Any) extends RSResult {
override def foreach (f:Any=>Unit) { f(res) }
override def map (f:Any=>Any): RSResult = RSSucc(f(res))
override def getOrElse (f: => Any) : Any = res
}
case class RSError (err:String) extends RSResult
object RSIncomplete extends RSResult // expression is incomplete...
object RSUnsupported extends RSResult // interactive mode unsupported
object RSSuccNoValue extends RSResult // successful, but no value returned
And then, of course, the methods returning these (gist):
def eval (ctx:ScriptContext) : RSResult
At this point, your client code can be much simplified and, instead of
script.eval (ctx) match {
case RSSucc(value) => println(value)
case _ => println ("some error we don't really care about right here")
}
you simply write:
script.eval (ctx).foreach(value=>println(value)}
//or even this form, if you get the signatures right
for (value <- script.eval (ctx)) println(value)
I used Any instead of [A] for simplicity, but there you have it: the Option monad pattern thing. Pretty cool! You can try the code right now in your browser here (click inside the editor and then Ctrl+F9).
Note - syntax hilighting using github.com/razie/hiscala.