Option monad in scala
how is meant to work Option monad? I'm browsing the scala api and there is an example (I mean the second one),
Because of how for comprehension works, if None is returned from request.getParameter, the entire expression results in None
But when I try this code:
val upper = for {
name <- None //request.getPara开发者_运维百科meter("name")
trimmed <- Some(name.trim)
upper <- Some(trimmed.toUpperCase) if trimmed.length != 0
} yield upper
println(upper.getOrElse(""))
I get a compile error. How is this supposed to work?
You get a compiler error because of this
name <- None
That way, the type of None
is set to None.type
and the variable name
is inferred to be of type Nothing
. (That is, it would have this type if it actually existed but obviously the for comprehension does not even get to creating it at runtime.) Therefore no method name.trim
exists and it won’t compile.
If you had request.getParameter("name")
available, its type would be Option[String]
, name
would potentially have type String
and name.trim
would compile.
You can work around this by specifying the type of None
:
name <- None: Option[String]
To expand on Kevin's answer, you can avoid wrapping values in Some()
by using the =
operator instead of the <-
operator:
val upper = for {
name <- None: Option[String] //request.getParameter("name")
trimmed = name.trim
upper = trimmed.toUpperCase if trimmed nonEmpty
} yield upper
The for-comprehension will compile to something very similar to Kevin's version, but I often find it more clear to use map
and filter
explicitly to avoid clutter (e.g. extra variable names) that add nothing to the semantic content of the expression.
To expand on Debilski's answer, you also don't need to explicitly wrap subsequent values in Some()
, the only value you're actually mapping over is the original name
.
A better approach would be be to use the map
and filter
operations directly instead of a for-comprehension:
NOTE: behind the scenes, the Scala compiler will convert a for-comprehension to a combination of map/flatMap/filter anyway, so this approach will never be less efficient than a for-comprehension, and may well be more efficient
def clean(x:Option[String]) = x map { _.trim.toUpperCase } filterNot { _.isEmpty }
val param = None : Option[String] // request.getParameter("name")
println( clean(param).getOrElse("") )
精彩评论