Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Scala for-yield return None if I pass in an Option to it?

Tags:

scala

I have the following for-yield loop which takes in a boolean and should either yield Some(string) or None, depending on the boolean:

val theBoolean = false

val x: Option[String] =
for {
  theArg <- theBoolean
} yield {
  if (theArg) {
    "abc"
  } else {
    None
  }
}

This works great if theBoolean is actually a Boolean like false. However if I wanted to pass in an Option[Boolean]:

val theBoolean = Some(false)

it seems like Scala automatically applies a Some() wrapper to the None return - I get a complaint that "Expression of type Option[Serializable] doesn't conform to expected type Option[String]" (with None being the Serializable). The yield is perfectly happy with the same string return though (it doesn't become an Option[Option[String]]

How would I return a None in this case?

like image 678
Nathan Avatar asked Nov 05 '14 00:11

Nathan


People also ask

How does for yield work in Scala?

Summary: Scala's 'yield' keywordFor each iteration of your for loop, yield generates a value which is remembered by the for loop (behind the scenes, like a buffer). When your for loop finishes running, it returns a collection of all these yielded values.

What is Scala yield return?

yield keyword will returns a result after completing of loop iterations. The for loop used buffer internally to store iterated result and when finishing all iterations it yields the ultimate result from that buffer.

Why yield is used in Scala?

Yield is a keyword in scala that is used at the end of the loop. We can perform any operation on the collection elements by using this for instance if we want to increment the value of collection by one. This will return us to the new collection.

What is for comprehension in Scala?

Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e , where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter.


1 Answers

A for-comprehension is just syntactic sugar for a series of flatMap, map and filter. Let's desugar your code then:

val theBoolean = Some(false)

val x = theBoolean.map { theArg =>
  if (theArg) {
    "abc"
  } else {
    None
  }
}

As you can see, you're just mapping over the value of the Option, so you'll either return Some(abc), Some(None) or None (in case theBoolean is already None).

The lowest common type of None and "abc" is java.Serializable, so that's why the type of x is inferred as Option[Serializable], which as meaningless as Option[Any].

Possible solutions are:

  • using a flatMap

    theBoolean.flatMap(theArg => if (theArg) Some("abc") else None)
    

    or even shorter

    theBoolean.flatMap(if (_) Some("abc") else None)
    
  • filtering and mapping

    theBoolean.withFilter(identity).map(_ => "abc")
    

Where I used identity since you're testing the value itself.

Clearly you can always leverage the syntactic sugar provided by a for-comprehension, although it doesn't really make a difference in this case

for {
  theArg <- theBoolean
  if theArg
} yield "abc"
like image 180
Gabriele Petronella Avatar answered Sep 29 '22 13:09

Gabriele Petronella