scala option scalaz

Convertir una lista de opciones en una opción de lista usando Scalaz



option (4)

Esto funcionó para mí. Espero que esta sea una solución correcta.

Devuelve Ninguno si una de las Opciones en la Lista es Ninguna, de lo contrario devuelve la Opción de Lista [A]

def sequence[A](a: List[Option[A]]): Option[List[A]] = { a.foldLeft(Option(List[A]())) { (prev, cur) => { for { p <- prev if prev != None x <- cur } yield x :: p } } }

Quiero transformar una List[Option[T]] en una Option[List[T]] . El tipo de firma de la función es

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

El comportamiento esperado es asignar una lista que contenga solo Some s en un Some contenga una lista de los elementos dentro de los elementos de Some . Por otro lado, si la lista de entrada tiene al menos un None , el comportamiento esperado es simplemente devolver None . Por ejemplo:

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())

Una implementación de ejemplo, sin scalaz, sería:

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]]; }}}

Recuerdo haber visto un ejemplo similar en alguna parte, pero usando Scalaz para simplificar el código. ¿Cómo se vería?

Una versión ligeramente más concisa, utilizando Scala2.8 PartialFunction.condOpt , pero aún sin 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 } }}


Hay una función que convierte una List[Option[A]] en una Option[List[A]] en Scalaz. Es sequence Para obtener None en caso de que alguno de los elementos sea None y un Some[List[A]] en caso de que todos los elementos sean Some , solo puede hacer esto:

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

Este método en realidad convierte F[G[A] en G[F[A]] dado que existe una implementación de Traverse[F] y de Applicative[G] (la Option y la List cumplen ambos requisitos y son proporcionadas por esas importaciones ).

La semántica de Applicative[Option] es tal que si alguno de los elementos de una List de Option es None , entonces la sequence también será None . Si desea obtener una lista de todos los valores de Some , independientemente de si otros valores son None , puede hacer esto:

lo flatMap (_.toList)

Puede generalizar que para cualquier Monad que también forme un Monoid (la List es una de estas):

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))


Por alguna razón no te gusta

if (lo.exists(_ isEmpty)) None else Some(lo.map(_.get))

? Ese es probablemente el más corto en Scala sin Scalaz.


Si bien la Applicative[Option] aplicativa en Scalaz tiene el comportamiento incorrecto para usar directamente la MA#sequence , también puede derivar un Applicative de un Monoid . Esto se hace conveniente con MA#foldMapDefault o MA#collapse .

En este caso, usamos un Monoid[Option[List[Int]] . Primero realizamos un mapa interno ( MA#∘∘ ) para envolver los Int individuales en la List de un elemento.

(List(some(1), none[Int], some(2)) ∘∘ {(i: Int) => List(i)}).collapse assert_≟ some(List(1, 2)) (List(none[Int]) ∘∘ {(i: Int) => List(i)}).collapse assert_≟ none[List[Int]] (List[Option[Int]]() ∘∘ {(i: Int) => List(i)}).collapse assert_≟ none[List[Int]]

Resumen de la List a cualquier contenedor con instancias de Traverse , Pointed y Monoid :

def co2oc[C[_], A](cs: C[Option[A]]) (implicit ct: Traverse[C], cp: Pointed[C], cam: Monoid[C[A]]): Option[C[A]] = (cs ∘∘ {(_: A).pure[C]}).collapse co2oc(List(some(1), none[Int], some(2))) assert_≟ some(List(1, 2)) co2oc(Stream(some(1), none[Int], some(2))) assert_≟ some(Stream(1, 2)) co2oc(List(none[Int])) assert_≟ none[List[Int]] co2oc(List[Option[Int]]()) assert_≟ none[List[Int]]

Lamentablemente, al intentar compilar este código actualmente, se activa el #2741 o se envía el compilador a un bucle infinito.

ACTUALIZACIÓN Para evitar atravesar la lista dos veces, debería haber usado foldMapDefault :

(List(some(1), none[Int], some(2)) foldMapDefault (_ ∘ ((_: Int).pure[List])))

Esta respuesta se basó en la solicitud original de que una lista vacía, o una lista que contenga solo None , debe devolver una None . Por cierto, esto sería mejor modelado por el tipo Option[scalaz.NonEmptyList] - NonEmptyList garantiza al menos un elemento.

Si solo desea una List[Int] , hay muchas maneras más fáciles, dadas en otras respuestas. Dos formas directas que no han sido mencionadas:

list collect { case Some(x) => x } list flatten