开发者

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜