Can Scala for-yield return None if I pass it an option?

I have the following for-yield loop that takes a boolean value and should either give 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 fine if theBoolean is actually Boolean, such as false . However, if I want to pass Option[Boolean] :

 val theBoolean = Some(false) 

it looks like Scala automatically applies the Some () wrapper to the None return. I get a complaint that "An expression of type Option [Serializable] does not match the expected type of Option [String]" (in the absence of None Serializable). The output perfectly agrees with the same string return (it does not become the [Option [String]] option

How can I return None in this case?

+6
source share
3 answers

For understanding, only syntactic sugar is understood for the flatMap , map and filter series. Let desugar your code then:

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

As you can see, you are simply matching the Option value, so you will either return Some(abc) , Some(None) or None (in case theBoolean already None ).

The lowest common type is None and "abc" is java.Serializable , so type x is output as Option[Serializable] , which is pointless like Option[Any] .

Possible solutions:

  • using flatMap

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

    or even shorter

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

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

Where I used identity , since you are testing the value itself.

It is clear that you can always use the syntactic sugar provided for understanding, although in fact it does not really matter.

 for { theArg <- theBoolean if theArg } yield "abc" 
+13
source

You are right, everything in the yield block is wrapped in an option (which, as for understanding, works for parameters). In general, for-understanding, it is understood what needs to be done with content that can be found inside a monad for which for-comprehension is used, but the final result (for the world outside the yield block) is still a monad of the same type (for example, Option , Try or List).

In an even more general note: there are many descriptions of what a monad is. You can assume that the monad is the notorious Schrödinger box, and you are pondering what might happen to this cat hidden there, but all this remains an opportunity, because the box is not yet open.

A possible way to do what you want:

 val theBoolean = false val x: Option[String] = for { theArg <- theBoolean if theArg } yield { "abc" } 
+1
source

Instead of understanding, it seems to you that you need a flat map instead of understanding.

 scala> Some(false).flatMap(if (_) Some("abc") else None) res4: Option[String] = None 
0
source

Source: https://habr.com/ru/post/977737/


All Articles