This is just some notes...To be continued and completed ... Updated on 2011-11-17
Interesting articles (and set of comments) to understand and recognize monads :
- Monads are not metaphors
- Monads are elephants

Exploring the best way to get a value from a map, or a default value if the key doesn't exist :

val codes=Map("A"->10, "B"->5, "C"->20)

{ // Basic approach
var code = 30
if (codes contains "D") code = codes.get("D").get
}

{ // More compact approach
val code = if (codes contains "D") codes.get("D").get else 30
}

{ // Match approach
val code = codes.get("D") match {
case Some(v)=>v
case None=>30
}
}

{ // Option monad approach
val code = codes get "D" getOrElse 30
}

{ // Compact approach
val code = codes.getOrElse("D",30)
}
With monads, many test cases can be avoided, the code looks like a data flow :

import collection.JavaConversions._
import java.io.File
val sp = java.lang.System.getProperties.toMap

val lookInto = sp.get("baseDir") orElse sp.get("rootDir") orElse sp.get("user.home")
val lookIntoDirFile = lookInto map {new File(_)} filter {_.exists}
val count = lookIntoDirFile map {_.list.size}

println(count map {_+" files"} getOrElse "Dir not found")

// will print "Dir not found" or for example "184 files"

An other example using Option Monad, the second fullName implementation becomes very simple:

// Using classical approach, a Java like approach
case class Person1(firstName:String, lastName:String) {
def fullName() = {
if (firstName!=null) {
if (lastName!=null) {
firstName+" "+lastName
} else null
} else null
}
}

{
val p1 = Person1("Einstein", "Albert")
val p2 = Person1("Aristote", null)
println("1-%s" format p1.fullName)
val fullName = if (p2.fullName!=null) p2.fullName else "Unknown"
println("1-%s" format fullName)
}

// Using Option monad approach
case class Person2(firstName:Option[String], lastName:Option[String]) {
def fullName() = firstName flatMap {fn => lastName map {ln => fn+" "+ln}}
}

{
val p1 = Person2(Some("Einstein"), Some("Albert"))
val p2 = Person2(Some("Aristote"), None)
println("2-%s" format p1.fullName.getOrElse("Unknown"))
println("2-%s" format p2.fullName.getOrElse("Unknown"))
}

// the Best and the cleanest, same usage as the previous one
case class Person3(firstName:Option[String], lastName:Option[String]) {
def fullName() = for(fn<-firstName ; ln <- lastName) yield fn+" "+ln
}


To be continued and completed ...