tags:

views:

147

answers:

2

Using Scala 2.7.7:

If I have a list of Options, I can flatten them using a for-comprehension:

val listOfOptions = List(None, Some("hi"), None)
listOfOptions: List[Option[java.lang.String]] = List(None, Some(hi), None)

scala> for (opt <- listOfOptions; string <- opt) yield string
res0: List[java.lang.String] = List(hi)

I don't like this style, and would rather use a HOF. This attempt is too verbose to be acceptable:

scala> listOfOptions.flatMap(opt => if (opt.isDefined) Some(opt.get) else None)
res1: List[java.lang.String] = List(hi)

Intuitively I would have expected the following to work, but it doesn't:

scala> List.flatten(listOfOptions)
<console>:6: error: type mismatch;
 found   : List[Option[java.lang.String]]
 required: List[List[?]]
       List.flatten(listOfOptions)

Even the following seems like it should work, but doesn't:

scala> listOfOptions.flatMap(_: Option[String])
<console>:6: error: type mismatch;
 found   : Option[String]
 required: (Option[java.lang.String]) => Iterable[?]
       listOfOptions.flatMap(_: Option[String])
                          ^

The best I can come up with is:

scala> listOfOptions.flatMap(_.toList)         
res2: List[java.lang.String] = List(hi)

... but I would much rather not have to convert the option to a list. That seems clunky.

Any advice?

+7  A: 

In Scala 2.8, flatten will work:


Welcome to Scala version 2.8.0.RC2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val listOfOptions = List(None, Some("hi"), None)
listOfOptions: List[Option[java.lang.String]] = List(None, Some(hi), None)

scala> listOfOptions flatten
res0: List[java.lang.String] = List(hi)

This doesn't work in 2.7.7, however:


Welcome to Scala version 2.7.7.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).

scala> val listOfOptions = List(None, Some("hi"), None)
listOfOptions: List[Option[java.lang.String]] = List(None, Some(hi), None)

scala> listOfOptions.flatten
:6: error: no implicit argument matching parameter type (Option[java.lang.String]) => Iterable[Nothing] was found.
       listOfOptions.flatten

The collections library has been redesigned, and has improved a lot in 2.8, so perhaps you might want to try to use the latest Scala 2.8 RC and see if that makes it more easy to use for you.

If you really don't want to use the toList method, I guess you can also write it like this:


scala> listOfOptions.flatMap(o => o)
res: List[java.lang.String] = List(hi)

Also not a thing of beauty perhaps, but at least this works in 2.7.7.

Arjan Blokzijl
Oh yes. Thank you! I did once stumble upon flatMap(o => o) but promptly forgot about it. Why flatMap(_) doesn't work equally as well is a curious thing.
Synesso
+8  A: 

To complement Arjan's answer, in Scala 2.7.7 you can use List#flatten, but you need to help out the type inferencer:

Welcome to Scala version 2.7.7.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val listOfOptions = List(None, Some("hi"), None)
listOfOptions: List[Option[java.lang.String]] = List(None, Some(hi), None)

scala> listOfOptions.flatten[String]                   
res0: List[String] = List(hi)

scala> val x: List[String] = listOfOptions.flatten
x: List[String] = List(hi)
retronym