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?
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.
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.
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.
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.
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"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With