Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
702 views
in Technique[技术] by (71.8m points)

scala - Convert a List of Options to an Option of List using Scalaz

I want to transform a List[Option[T]] into a Option[List[T]]. The signature type of the function is

def lo2ol[T](lo: List[Option[T]]): Option[List[T]]

The expected behavior is to map a list that contains only Somes into a Some containing a list of the elements inside the elements Some's. On the other hand, if the input list has at least one None, the expected behavior is to just return None. For example:

scala> lo2ol(Some(1) :: Some(2) :: Nil)
res10: Option[List[Int]] = Some(List(1, 2))

scala> lo2ol(Some(1) :: None :: Some(2) :: Nil)
res11: Option[List[Int]] = None

scala> lo2ol(Nil : List[Option[Int]])
res12: Option[List[Int]] = Some(List())

An example implementation, without scalaz, would be:

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] = {
  lo.foldRight[Option[List[T]]](Some(Nil)){(o, ol) => (o, ol) match {
    case (Some(x), Some(xs)) => Some(x :: xs);
    case _ => None : Option[List[T]]; 
}}}

I remember seeing somewhere a similar example, but using Scalaz to simplify the code. How would it look like?


A slightly more succinct version, using Scala2.8 PartialFunction.condOpt, but still without Scalaz:

import PartialFunction._

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] = {
  lo.foldRight[Option[List[T]]](Some(Nil)){(o, ol) => condOpt(o, ol) {
    case (Some(x), Some(xs)) => x :: xs
  }
}}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

There's a function that turns a List[Option[A]] into an Option[List[A]] in Scalaz. It's sequence. To get None in case any of the elements are None and a Some[List[A]] in case all the elements are Some, you can just do this:

import scalaz.syntax.traverse._
import scalaz.std.list._     
import scalaz.std.option._

lo.sequence

This method actually turns F[G[A] into G[F[A]] given that there exists an implementation of Traverse[F], and of Applicative[G] (Option and List happen to satisfy both and are provided by those imports).

The semantics of Applicative[Option] are such that if any of the elements of a List of Options are None, then the sequence will be None as well. If you want to get a list of all the Some values regardless of whether any other values are None, you can do this:

lo flatMap (_.toList)

You can generalize that for any Monad that also forms a Monoid (List happens to be one of these):

import scalaz.syntax.monad._

def somes[F[_],A](x: F[Option[A]])
                 (implicit m: Monad[F], z: Monoid[F[A]]) =
  x flatMap (o => o.fold(_.pure[F])(z.zero))

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...