Help me get this for comprehension right
I have the following (working) code
val superSuperSorts = superSorts.flatMap(relations.get(_)).flatMap(x=>x)
And I give you the types here
val superSorts: Set[Sort]
val relations: Map[Sort, Set[Sort]]
Changing it to this for-comprehension gives me a compile error
val superSuperSorts =
for(
ss <- superSorts;
sss <- relations.get(ss); //get Option[Set[Sort]] and flatten the option
s <- sss //extract the elements from the Set
) yield s
of this reading
error: type mismatch;
found : scala.collection.immutable.Set[edu.uulm.scbayes.logic.Sort]
required: Option[?]
s开发者_Python百科 <- sss
Please explain why my for-comprehension is wrong.
You can't flatMap
an Option. Have a look at its type signature:
def flatMap [B] (f: (A) ⇒ Option[B]): Option[B]
So, flatMap
unpacks the Option, but requires a new Option, so you need an alternative. You can use the method getOrElse
of Map or method seq
of Option:
val superSuperSorts = for {
s <- superSorts
ss <- relations.getOrElse(s, Set.empty)
} yield s
val superSuperSorts = for {
s <- superSorts
ss <- relations.get(s).seq
sss <- ss
} yield sss
Another problem is, that your flatMap
code is not equivalent with your for-expression. The expression
for (x <- expr1; y <- expr2) yield expr3
is translated to
expr1.flatMap(x => for (y <- expr2) yield expr3)
and in another step to
expr1.flatMap(x => expr2.map(y => expr3))
But you have:
expr1.flatMap(x => expr2).flatMap(y => expr3)
Let me give an example to make clear where the problem is. Let's say you have:
val superSorts = Set('x)
val relations = Map('x -> Set('a, 'b))
This code:
val superSuperSorts =
for(
ss <- superSorts;
sss <- relations.get(ss); //get Option[Set[Sort]] and flatten the option
s <- sss //extract the elements from the Set
) yield s
translates to:
superSorts.flatMap(
ss => relations.get(ss).flatMap(
sss => sss.map(
s => s)))
First, note that the last term is a map
, not a flatMap
. Now, let's consider an iteration with the data above:
ss = 'x
sss = Set('a, 'b)
s = 'a, 'b
And now let's go backward in the code.
// Set('a, 'b) => Set('a, 'b)
sss.map(s => s)
// Some(Set('a, 'b)) => Some('a, 'b)????
relations.get(ss).flatMap(...)
See the problem here? How can an Option[Set[Sort]]
be flattened? There's no such a thing as a Some('a, 'b)
.
So, why does the original code work?
val superSuperSorts = superSorts.flatMap(relations.get(_)).flatMap(x=>x)
If we break it down:
// Set('x) => Set(Set('a, 'b))
superSorts.flatMap(relations.get(_))
// Set(Set('a, 'b')) => Set('a, 'b)
(...).flatMap(x=>x)
See how all the flatMap
are being applied over a Set
, not an Option
? The Option
was eliminated by the flatMap
and never came into play.
The more-or-less equivalent for-comprehension for your code would be:
val superSuperSorts = for {
x <- (for {
ss <- superSorts
sss <- relations.get(ss)
} yield sss)
s <- x
} yield s
This introduces a couple of identity maps: map(sss => sss)
and map(s => s)
, which get around the fact that the last generator in a for-comprehension is always a map
.
It's important to understand that a for comprehension will yield the type of collection fed in. So why your code didn't work is you want to return a single element(s). Try this:
val superSuperSorts =
for(
ss <- superSorts;
sss <- relations.get(ss); //get Option[Set[Sort]] and flatten the option
) yield sss
:/ Hope this helps
精彩评论