Pergunta

Option mônada é uma ótima maneira expressiva para lidar com as coisas algo ou nada em Scala. Mas e se a pessoa precisa registrar uma mensagem quando "nada" ocorre? De acordo com a documentação da API Scala,

O tipo Ou é frequentemente utilizado como um alternativa para scala.Option onde Esquerda representa falha (por convenção) e Direita é semelhante para alguns.

No entanto, não tive sorte de encontrar as melhores práticas usando tanto ou bons exemplos do mundo real envolvendo quer por falhas de processamento. Finalmente eu vim acima com o seguinte código para o meu próprio projeto:

    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 <- props.toRight('warning -> "ConfigurationAdmin service not bound").right
                 val logsParam <- properties.get("logs").toRight('debug -> "'logs' not defined in the configuration").right
                 val array <- checkType(logsParam).toRight('warning -> "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)
    }

(Por favor note que este é um trecho de um projeto real, por isso não irá compilar por conta própria)

Eu ficaria grato de saber como você está usando Either em seu código e / ou melhores idéias sobre refatoração do código acima.

Foi útil?

Solução

De qualquer é usada para retornar um dos possíveis dois resultados significativos, ao contrário de Opção, que é usada para retornar um único resultado significativo ou nada.

Um exemplo fácil de compreender é dado abaixo (circulado na lista de discussão Scala um tempo atrás):

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

Como o nome da função indica, se a execução do "bloco" é bem sucedido, ele retornará "Right ()". Caso contrário, se um Throwable é lançado, ele retornará "Left ()". Use a correspondência de padrões para processar o 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 ajude.

Outras dicas

biblioteca Scalaz tem algo semelhante Ou chamado validação. É mais idiomática do que qualquer um para uso como "obter qualquer resultado válido ou um fracasso".

A validação também permite erros acumular.

Edit: "iguais" Ou é complettly falso, porque Validação é um functor aplicativo, e scalaz Ou, chamado \ / (pronuncia-se "disjonction" ou "quer"), é uma mônada. O fato de que a validação pode accumalate erros é por causa do que a natureza. Por outro lado, / tem uma "parada cedo" natureza, parando na primeira - \ / (lê-lo "esquerda", ou "erro") que encontra. Há uma explicação perfeita aqui: http://typelevel.org/blog /2014/02/21/error-handling.html

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

A pedido do comentário, copiar / colar do link acima (algumas linhas removido):

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

O trecho que você postou parece muito artificial. Você usar em uma situação onde:

  1. Não é suficiente apenas conhecer os dados não está disponível.
  2. Você precisa retornar um dos dois tipos distintos.

Rodar uma excepção para um Esquerda é, de facto, um caso de uso comum. Ao longo try / catch, ele tem a vantagem de manter o código em conjunto, o que faz sentido se a exceção é um resultado esperado . A forma mais comum de lidar com qualquer é o padrão de harmonização:

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

Outra forma interessante de lidar com Either é quando ele aparece em uma coleção. Ao fazer um mapa sobre uma coleção, lançando uma exceção pode não ser viável, e você pode querer retornar alguma informação diferente de "não é possível". Usando um Ou permite que você faça isso sem sobrecarregar o algoritmo:

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

Aqui temos uma lista de todos os autores na biblioteca, mais uma lista de livros sem um autor. Assim, podemos então ainda processá-lo de acordo:

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

Assim, o uso básico ou vai assim. Não é uma classe particularmente útil, mas se fosse você teria visto isso antes. Por outro lado, não é inútil também.

Cats tem uma boa maneira de criar um Ou a partir do código-jogando exceção:

val either: Either[NumberFormatException, Int] =
  Either.catchOnly[NumberFormatException]("abc".toInt)
// either: Either[NumberFormatException,Int] = Left(java.lang.NumberFormatException: For input string: "abc")

em https : //typelevel.org/cats/datatypes/either.html#working-with-exception-y-code

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top