Question

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.getParameter("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?

Was it helpful?

Solution

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]

OTHER TIPS

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("") )
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top