scala monads typeclass for-comprehension either

Usando Eithers con Scala "para" la sintaxis



monads typeclass (3)

¿Esta funcionalidad está disponible a través de alguna importación?

Sí, pero a través de una importación de terceros: Scalaz proporciona una instancia de Monad para Either .

import scalaz._, Scalaz._ scala> for { | foo <- 1.right[String] | bar <- "nope".left[Int] | } yield (foo.toString + bar) res39: Either[String,java.lang.String] = Left(nope)

Ahora if -guard no es una operación monádica. Por lo tanto, si intenta usar if -guard, se genera un error de compilación como se esperaba.

scala> for { | foo <- 1.right[String] | if foo > 3 | } yield foo <console>:18: error: value withFilter is not a member of Either[String,Int] foo <- 1.right[String] ^

Los métodos de conveniencia utilizados anteriormente - .right y .left - también son de Scalaz.

Editar:

Me perdí esta pregunta tuya.

Supongamos que quisiera proporcionar mi propia "instancia de mónada" para O bien, ¿cómo podría hacer eso?

Las comprensiones de Scala se traducen simplemente en .map , .flatMap , .withFilter y .filter .foreach invoca los objetos implicados. ( here puede encontrar el esquema completo de traducción.) De modo que si alguna clase no tiene los métodos requeridos, puede agregarlos a una clase utilizando conversiones implícitas.

Una nueva sesión de REPL a continuación.

scala> implicit def eitherW[A, B](e: Either[A, B]) = new { | def map[B1](f: B => B1) = e.right map f | def flatMap[B1](f: B => Either[A, B1]) = e.right flatMap f | } eitherW: [A, B](e: Either[A,B])java.lang.Object{def map[B1](f: B => B1): Product with Either[A,B1] with Serializable; def flatMap[B1](f: B => Either[A,B1]): Either[A,B1]} scala> for { | foo <- Right(1): Either[String, Int] | bar <- Left("nope") : Either[String, Int] | } yield (foo.toString + bar) res0: Either[String,java.lang.String] = Left(nope)

Tal como lo entiendo, la sintaxis "para" Scala es extremadamente similar a la sintaxis mondarea de "do" de Haskell. En Scala, la sintaxis "a favor" se usa a menudo para las List y las Option . Me gustaría usarlo con Either los Either , pero los métodos necesarios no están presentes en las importaciones predeterminadas.

for { foo <- Right(1) bar <- Left("nope") } yield (foo + bar) // expected result: Left("nope") // instead I get "error: value flatMap is not a member..."

¿Esta funcionalidad está disponible a través de alguna importación?

Hay un pequeño problema:

for { foo <- Right(1) if foo > 3 } yield foo // expected result: Left(???)

Para una lista, sería List() . Para Option , sería None . ¿Las bibliotecas estándar de Scala proporcionan una solución a esto? (¿O tal vez scalaz ?) ¿Cómo? Supongamos que quisiera proporcionar mi propia "instancia de mónada" para O bien, ¿cómo podría hacer eso?


A partir de Scala 2.12, Either está ahora predispuesto

De la documentation :

Como Oither define el mapa de métodos y el mapa plano, también se puede usar para las comprensiones:

val right1: Right[Double, Int] = Right(1) val right2 = Right(2) val right3 = Right(3) val left23: Left[Double, Int] = Left(23.0) val left42 = Left(42.0) for ( a <- right1; b <- right2; c <- right3 ) yield a + b + c // Right(6) for ( a <- right1; b <- right2; c <- left23 ) yield a + b + c // Left(23.0) for ( a <- right1; b <- left23; c <- right2 ) yield a + b + c // Left(23.0) // It is advisable to provide the type of the “missing” value (especially the right value for `Left`) // as otherwise that type might be infered as `Nothing` without context: for ( a <- left23; b <- right1; c <- left42 // type at this position: Either[Double, Nothing] ) yield a + b + c // ^ // error: ambiguous reference to overloaded definition, // both method + in class Int of type (x: Char)Int // and method + in class Int of type (x: Byte)Int // match argument types (Nothing)


No funciona en Scala 2.11 y versiones anteriores porque Either no es una mónada. Aunque se habla de un sesgo correcto, no se puede usar en una comprensión LeftProject : se debe obtener un LeftProject o RightProjection , como se muestra a continuación:

for { foo <- Right[String,Int](1).right bar <- Left[String,Int]("nope").right } yield (foo + bar)

Eso regresa a la Left("nope") , por cierto.

En Scalaz, reemplazaría Oither with Validation . Dato curioso: el autor original de Either los Either es Tony Morris, uno de los autores de Scalaz. Quería hacer que Either bien se Either derecha, pero un colega lo convenció de lo contrario.