null-safe和scala的monad


长久以来,习惯了写如下的Java代码:

User user = userService.get(1);
if(user != null) {
  doStuff();
}

称之为null检查,亦有null != user的尤达式写法。
在学了Scala之后,知道了Option,改用如下写法:

Option(userService.get(1)) match {
  case Some(user) => doStuff()
  case _ => // do nothing
}

最近又学会了一种新写法:

Option(userService.get(1)).foreach(user => doStuff())

对于单个null检查来说,上面的代码的足够了。foreach起的作用就是把some和none当作列表,很聪明的做法。
但是我又遇到了多个null检查,如果是Java代码的话,这么写:

User user = userService.get(1)
if(user != null) {
  Address address = user.getAddress()
  if(address != null) {
    doStuff()
  }
}

如果直接用Scala的Option实现上面的逻辑,可能是嵌套的match或者是函数加flatMap的形式。
其实我在写博客前遇到了另外一个问题:

val url: Option[String] = Some("http:/www.google.com/")
val queryString: Option[String] = Some("q=foo")

println(url.getOrElse("") + queryString.getOrElse(""))

有点代码坏味。这个问题和user.address有什么关系么?回答是两者都能用monad解决。
针对url+querystring问题我google出来的结果提到了scala中的monad思路,作为学过一点haskell但是没弄懂monad的人看过下面的代码恍然大误:

val url: Option[String] = Some("http://www.google.com/")
val queryString: Option[String] = Some("q=foo")

val url = for (u <- url; qs <- queryString) yield u + qs
println(url)

Scala的for在这里起到了haskell中的do的作用。
回到user.address的问题,使用Scala的monad思路的代码如下:

for (
  user <- Option(userService.get(1)); 
  address <- Option(user.getAddress())
) doStuffWithUserAndAddress();

是不是比二次null检查要清楚多了?
更多资料请google scala monad。