Usando O bien para procesar fallas en el código de Scala

Option mónada de Option es una gran manera expresiva de lidiar con cosas de algo o nada en Scala. Pero, ¿qué ocurre si uno necesita registrar un mensaje cuando ocurre “nada”? De acuerdo con la documentación de la API de Scala,

El tipo Either se usa a menudo como alternativa a Scala. Opción donde Left representa el fracaso (por convención) y Right es similar a Some.

Sin embargo, no tuve la suerte de encontrar las mejores prácticas utilizando O bien uno o buenos ejemplos del mundo real que implican O bien para el procesamiento de fallas. Finalmente, he encontrado el siguiente código para mi propio proyecto:

  def logs: Array[String] = { def props: Option[Map[String, Any]] = configAdmin.map{ ca => val config = ca.getConfiguration(PID, null) config.properties getOrElse immutable.Map.empty } def checkType(any: Any): Option[Array[String]] = any match { case a: Array[String] => Some(a) case _ => None } def lookup: Either[(Symbol, String), Array[String]] = for {val properties  "ConfigurationAdmin service not bound").right val logsParam  "'logs' not defined in the configuration").right val array  "unknown type of 'logs' confguration parameter").right} yield array lookup.fold(failure => { failure match { case ('warning, msg) => log(LogService.WARNING, msg) case ('debug, msg) => log(LogService.DEBUG, msg) case _ => }; new Array[String](0) }, success => success) } 

(Tenga en cuenta que este es un fragmento de un proyecto real, por lo que no se comstackrá por sí mismo)

Estaría agradecido de saber cómo está usando Either en su código y / o mejores ideas sobre la refacturación del código anterior.

Cualquiera de los dos se usa para devolver uno de los dos posibles resultados significativos, a diferencia de la opción que se usa para devolver un único resultado significativo o nada.

Un ejemplo fácil de entender se da a continuación (distribuido en la lista de correo de Scala hace un tiempo):

 def throwableToLeft[T](block: => T): Either[java.lang.Throwable, T] = try { Right(block) } catch { case ex => Left(ex) } 

Como el nombre de la función implica, si la ejecución de “bloque” es exitosa, devolverá “Derecha ()”. De lo contrario, si arroja un Throwable, devolverá “Left ()”. Use la coincidencia de patrones para procesar el resultado:

 var s = "hello" throwableToLeft { s.toUpperCase } match { case Right(s) => println(s) case Left(e) => e.printStackTrace } // prints "HELLO" s = null throwableToLeft { s.toUpperCase } match { case Right(s) => println(s) case Left(e) => e.printStackTrace } // prints NullPointerException stack trace 

Espero que ayude.

La biblioteca de Scalaz tiene algo similar. O bien se llama Validación. Es más idiomático que O bien para su uso como “obtener un resultado válido o una falla”.

La validación también permite acumular errores.

Editar: “similar” Cualquiera de las dos es completamente falso, porque la Validación es un funcionador aplicativo, y scalaz O bien, llamado \ / (pronunciado “disjonction” o “either”), es una mónada. El hecho de que la Validación puede acumular errores se debe a esa naturaleza. Por otro lado, / tiene una naturaleza de “detenerse temprano”, deteniéndose al principio – \ / (léalo “izquierda” o “error”) que encuentra. Aquí hay una explicación perfecta: http://typelevel.org/blog/2014/02/21/error-handling.html

Ver: http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/example/ExampleValidation.scala.html

Según lo solicitado por el comentario, copie / pegue el enlace anterior (algunas líneas se eliminaron):

 // Extracting success or failure values val s: Validation[String, Int] = 1.success val f: Validation[String, Int] = "error".fail // It is recommended to use fold rather than pattern matching: val result: String = s.fold(e => "got error: " + e, s => "got success: " + s.toString) s match { case Success(a) => "success" case Failure(e) => "fail" } // Validation is a Monad, and can be used in for comprehensions. val k1 = for { i <- s j <- s } yield i + j k1.toOption assert_≟ Some(2) // The first failing sub-computation fails the entire computation. val k2 = for { i <- f j <- f } yield i + j k2.fail.toOption assert_≟ Some("error") // Validation is also an Applicative Functor, if the type of the error side of the validation is a Semigroup. // A number of computations are tried. If the all success, a function can combine them into a Success. If any // of them fails, the individual errors are accumulated. // Use the NonEmptyList semigroup to accumulate errors using the Validation Applicative Functor. val k4 = (fNel <**> fNel){ _ + _ } k4.fail.toOption assert_≟ some(nel1("error", "error")) 

El fragmento que publicaste parece muy artificial. Usted usa Cualquiera en una situación donde:

  1. No es suficiente simplemente saber que los datos no están disponibles.
  2. Debe devolver uno de dos tipos distintos.

Convertir una excepción en una izquierda es, de hecho, un caso de uso común. Durante la prueba / captura, tiene la ventaja de mantener el código en conjunto, lo que tiene sentido si la excepción es un resultado esperado . La forma más común de manejar O bien es la coincidencia de patrones:

 result match { case Right(res) => ... case Left(res) => ... } 

Otra forma interesante de manejarlo es cuando aparece en una colección. Al hacer un mapa sobre una colección, lanzar una excepción puede no ser viable, y es posible que desee devolver alguna información que no sea “imposible”. Usar uno de los dos permite hacerlo sin sobrecargar el algoritmo:

 val list = ( library \\ "books" map (book => if (book \ "author" isEmpty) Left(book) else Right((book \ "author" toList) map (_ text)) ) ) 

Aquí obtenemos una lista de todos los autores en la biblioteca, más una lista de libros sin autor. Entonces podemos procesarlo en consecuencia:

 val authorCount = ( (Map[String,Int]() /: (list filter (_ isRight) map (_.right.get))) ((map, author) => map + (author -> (map.getOrElse(author, 0) + 1))) toList ) val problemBooks = list flatMap (_.left.toSeq) // thanks to Azarov for this variation 

Por lo tanto, el uso básico o bien es así. No es una clase particularmente útil, pero si lo fuera lo hubieras visto antes. Por otro lado, tampoco es inútil.

Intereting Posts