What is the equivalent of Haskell\'s sequence in Scala? I want to turn list of options into an option of list. It should come out as None
if any of the options
Here is the same function as above using a combination of foldRight and map/ flatmap that only has to traverse the list once:
def sequence[A](lo: List[Option[A]]): Option[List[A]] =
lo.foldRight (Option(List[A]())) { (opt, ol) =>
ol flatMap (l => opt map (o => o::l))
}
Or, if you prefer the for comprehension version:
def sequence2[A](lo: List[Option[A]]): Option[List[A]] =
lo.foldRight (Option(List[A]())) { (opt, ol) =>
for {l <- ol; o <- opt} yield (o::l)
}
Scalaz defines sequence.
Here's an example:
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> List(Some(1), None, Some(2)).sequence
res0: Option[List[Int]] = None
scala> List(some(1), some(2), some(3)).sequence
res1: Option[List[Int]] = Some(List(1, 2, 3))
Note that in the second example, you have to use Scalaz's some function to create a Some -- otherwise, the List is constructed as List[Some[Int]], which results in this error:
scala> List(Some(1), Some(2), Some(3)).sequence
<console>:14: error: could not find implicit value for parameter n: scalaz.Applicative[N]
List(Some(1), Some(2), Some(3)).sequence
The Scalaz some(a) and none functions create Some and None values of type Option[A].
Since you need to flatten anyway, just do it first...
def sequence(lo: List[Option[A]]): Option[List[A]] = lo.flatten match {
la: List[A] if(la.length == lo.length) => Some(la)
_ => None
}
tail recursion might be quickest
First off, I recommend that you check out the API docs for List.
As for a solution, this may not be the most graceful way to do it, but it'll work (and with no external dependencies):
// a function that checks if an option is a None
def isNone(opt:Option[_]) = opt match {
case None => true
case _ => false
}
//templated for type T so you can use whatever Options
def optionifyList[T](list:List[Option[T]]) = list.exists(isNone) match {
case true => None
case false => Some(list.flatten)
}
And a test just to be sure...
scala> val hasNone = Some(1) :: None :: Some(2) :: Nil
hasNone: List[Option[Int]] = List(Some(1), None, Some(2))
scala> val hasSome = Some(1) :: Some(2) :: Some(3) :: Nil
hasSome: List[Some[Int]] = List(Some(1), Some(2), Some(3))
scala> optionifyList(hasSome)
res2: Option[List[Int]] = Some(List(1, 2, 3))
scala> optionifyList(hasNone)
res3: Option[List[Int]] = None
This is very simple with a for comprehension:
val x : Option[String] = Option("x")
val y : Option[String] = Option("y")
val z : Option[String] = None
// Result -> a: Option[List[String]] = None
val a = for {
x <- x
y <- y
z <- z
} yield List(x,y,z)
// Result -> b: Option[List[String]] = Some(List(x, y))
val b = for {
x <- x
y <- y
} yield List(x,y)
If you want a solution for just List and Option rather a general monad then following will do the job,
def sequence[T](l : List[Option[T]]) =
if (l.contains(None)) None else Some(l.flatten)
REPL session,
scala> sequence(List(Some(1), None, Some(2)))
res2: Option[List[Int]] = None
scala> sequence(List(Some(1), Some(2), Some(3)))
res3: Option[List[Int]] = Some(List(1, 2, 3))
Just use Scalaz ...