operator for ciclo scala monads for-comprehension

ciclo - scala for



for..else para tipos de opciones en Scala? (8)

¿Por qué algo como esto no funcionaría?

val opts = List[Option[Int]](Some(1), None, Some(2)) if (opts contains None) { // Has a None } else { // Launch the missiles val values = opts.map(_.get) // We know that there is no None in the list so get will not throw }

Supongamos que tengo dos Opciones y, si ambas son Algunas, ejecuto una ruta de código, y si nota, ejecuta otra. Me gustaria hacer algo como

for (x <- xMaybe; y <- yMaybe) { // do something } else { // either x or y were None, handle this }

Fuera de las afirmaciones if o la coincidencia de patrones (que podría no escalarse si tuviera más de dos opciones), ¿hay una mejor manera de manejar esto?


Creo que el punto clave aquí es pensar en términos de tipos como lo que quieres hacer. Como lo entiendo, usted desea recorrer una lista de pares de opciones y luego hacer algo basado en una determinada condición. Entonces, la parte interesante de su pregunta sería: ¿qué aspecto tendría el tipo de retorno, excepto usted? Creo que se vería algo así: O bien [Lista [Opción], Lista [Opción, Opción]]. en el lado del error (izquierda) acumularía la opción que se emparejó con una Ninguno (y se dejó solo, por así decirlo). En el lado derecho, usted suma las opciones no vacías que representan sus valores exitosos. Así que solo necesitamos una función que haga exactamente eso. Valide cada par y acumúlelo según su resultado (éxito - fracaso). Espero que esto ayude, si no explique con más detalle su caso de uso. Algunos enlaces para implementar lo que describí: http://applicative-errors-scala.googlecode.com/svn/artifacts/0.6/pdf/index.pdf y: http://blog.tmorris.net/automated-validation-with-applicatives-and-semigroups-for-sanjiv/


La función traverse en Scalaz generaliza tu problema aquí. Se necesitan dos argumentos:

  1. T[F[A]]
  2. A => F[B]

y devuelve F[T[B]] . La T es cualquier estructura de datos transitable, como List y la F es cualquier functor aplicativo como Option . Por lo tanto, para especializarse, su función deseada tiene este tipo:

  • List[Option[A]] => (A => Option[B]) => Option[List[B]]

Así que ponga todos sus valores de Option en una List

  • val z = List(xMaybe, yMaybe)

Construye la función que obtuviste, sin embargo, quieres recopilar los resultados:

  • val f: X => Opción [Y] = ...

y llamada traverse

  • val r = z transversal f

Estos patrones de programación ocurren muy a menudo. Tiene un papel que habla de todo, La esencia del patrón de iterador .

nota: solo quería corregir la URL, pero la ayuda de edición de CLEVER me dice que necesito cambiar al menos 6 caracteres, así que también incluyo este enlace útil (ejemplos de scala):
http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html


Muy cerca de su propuesta de sintaxis utilizando el yield para envolver la salida en una opción:

val result = { for (x <- xMaybe; y <- yMaybe) yield { // do something } } getOrElse { // either x or y were None, handle this }

El bloque getOrElse se ejecuta solo si una o ambas opciones son Ninguna.


Para escalar a muchas opciones, intente algo a lo largo de estas líneas:

def runIfAllSome[A](func:(A)=>Unit, opts:Option[A]*) = { if(opts.find((o)=>o==None) == None) for(opt<-opts) func(opt.get) }

Con esto, puedes hacer:

scala> def fun(i:Int) = println(i) fun: (i: Int)Unit scala> runIfAllSome(fun, Some(1), Some(2)) 1 2 scala> runIfAllSome(fun, None, Some(1)) scala>


Podría hacer coincidir el patrón de ambas Options al mismo tiempo:

(xMaybe, yMaybe) match { case (Some(x), Some(y)) => "x and y are there" case _ => "x and/or y were None" }


Si no sabe la cantidad de valores con los que está tratando, entonces la respuesta de Tony es la mejor. Si conoce la cantidad de valores con los que está tratando, le sugiero que utilice un functor aplicativo.

((xMaybe |@| yMaybe) { (x, y) => /* do something */ }).getOrElse(/* something else */)


Usted dijo que desea que la solución sea escalable :

val optional = List(Some(4), Some(3), None) if(optional forall {_.isDefined}) { //All defined } else { //At least one not defined }

EDIT: Acabo de ver que la solución de Emil Ivanov es un poco más elegante.