Question

Option monade est un excellent moyen d'expression pour faire face à des choses quelque chose ou rien à Scala. Mais si l'on a besoin de se connecter un message lorsque « rien » se produit? Selon la documentation de l'API Scala,

  

Le type ou l'autre est souvent utilisé comme   alternative à gauche où scala.Option   représente l'échec (par convention), et   Droit est semblable à certains.

Cependant, je n'avais pas la chance de trouver les meilleures pratiques utilisant ou bien des exemples concrets impliquant Que ce soit pour les échecs de traitement. Enfin, je suis venu avec le code suivant pour mon propre projet:

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

(S'il vous plaît noter que ceci est un extrait d'un projet réel, donc il ne sera pas compiler sur son propre)

Je serais reconnaissant de savoir comment vous utilisez Either dans votre code et / ou de meilleures idées sur la refactorisation du code ci-dessus.

Était-ce utile?

La solution

Soit est utilisé pour renvoyer l'un des deux possibles résultats significatifs, contrairement à l'option qui est utilisée pour renvoyer un seul résultat significatif ou rien.

Un exemple facile à comprendre est donné ci-dessous (diffusé sur la liste de diffusion Scala un certain temps):

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

Comme le nom de la fonction implique, si l'exécution du « bloc » est un succès, il retournera « droit () ». Dans le cas contraire, si un Throwable est jeté, il retournera « Gauche () ». Utiliser la correspondance de motif pour traiter le résultat:

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

L'espoir qui aide.

Autres conseils

bibliothèque Scalaz a quelque chose à l'identique soit nommé validation. Il est plus idiomatiques que ce soit pour être utilisé comme « obtenir soit un résultat valide ou un échec ».

La validation permet également d'accumuler des erreurs.

Edit: « aussi bien » Soit est complettly faux, parce que la validation est un foncteur applicatif et scalaz Soit, nommé \ / (prononcé « disjonction » ou « non »), est une monade. Le fait que la validation peut accumalate erreurs est à cause de cette nature. D'autre part, / a un caractère « stop précoce », arrêtant au premier - \ / (lire « gauche » ou « erreur ») rencontre. Il y a une explication parfaite ici: http://typelevel.org/blog /2014/02/21/error-handling.html

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

Comme demandé par le commentaire, copier / coller du lien ci-dessus (certaines lignes supprimées):

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

L'extrait que vous avez publié semble très artificiel. Vous utilisez Soit dans une situation où:

  1. Il ne suffit pas de savoir que les données ne sont pas disponibles.
  2. Vous devez retourner l'un des deux types distincts.

Transformer une exception en gauche est, en effet, un cas d'utilisation commune. Au cours try / catch, il a l'avantage de garder le code ensemble, ce qui est logique si l'exception est un résultat attendu . La façon la plus courante de la manipulation ou l'autre est correspondance de motif:

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

Une autre façon intéressante de traiter Either est quand il apparaît dans une collection. Quand vous faites une carte sur une collection, lancer une exception pourrait ne pas être viable, et vous pouvez retourner des informations autres que « pas possible ». L'utilisation d'un ou l'autre vous permet de le faire sans surcharger l'algorithme:

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

Ici, nous avons une liste de tous les auteurs dans la bibliothèque, plus une liste de livres sans auteur. Ainsi, nous pouvons alors traiter plus en conséquence:

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

Alors, va base Soit l'utilisation comme ça. Ce n'est pas une classe particulièrement utile, mais si ce que vous étiez aurait vu avant. D'autre part, il est inutile ne soit.

Les chats ont une belle façon de créer un ou l'autre à partir du code-exception lancer:

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

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top