web-dev-qa-db-fra.com

Option de conversion en soit en Scala

Supposons que je dois convertir Option[Int] à Either[String, Int] à Scala. J'aimerais le faire comme ceci:

def foo(ox: Option[Int]): Either[String, Int] =
  ox.fold(Left("No number")) {x => Right(x)}

Malheureusement, le code ci-dessus ne se compile pas et je dois ajouter le type Either[String, Int] explicitement:

ox.fold(Left("No number"): Either[String, Int]) { x => Right(x) }

Est-il possible de convertir Option en Either de cette façon sans ajouter le type?
Comment proposez-vous de convertir Option en Either?

24
Michael

Non, si vous le faites de cette façon, vous ne pouvez pas laisser de côté le type.

Le type de Left("No number") est supposé être Either[String, Nothing]. À partir de simplement Left("No number"), le compilateur ne peut pas savoir que vous voulez que le deuxième type de Either soit Int, et l'inférence de type ne va pas si loin que le compilateur examinera toute la méthode et décidera qu'elle doit être Either[String, Int].

Vous pouvez le faire de différentes manières. Par exemple, avec la correspondance de motifs:

def foo(ox: Option[Int]): Either[String, Int] = ox match {
  case Some(x) => Right(x)
  case None    => Left("No number")
}

Ou avec une expression if:

def foo(ox: Option[Int]): Either[String, Int] =
  if (ox.isDefined) Right(ox.get) else Left("No number")

Ou avec Either.cond:

def foo(ox: Option[Int]): Either[String, Int] =
  Either.cond(ox.isDefined, ox.get, "No number")
41
Jesper

Je ne sais pas quelle version de Scala vous utilisiez à l'époque. Actuellement, avec Scala 2.12.6 il n'y a pas de problèmes de compilation avec votre code comme celui-ci:

def foo(ox: Option[Int]): Either[String, Int] =
  ox.toRight("No number")

Un autre point que j'aimerais souligner est que le pliage (bien que ce soit ma méthode préférée pour réduire à peu près tout ce qui a une méthode de pliage) nécessite assez souvent de l'aide avec les paramètres de type. Le compilateur peut taper de deux manières une expression, soit il peut déduire les paramètres de type, soit il peut simplement les trouver explicitement définis.

Dans votre exemple, si vous essayez de plier une option comme ceci:

def foo(ox: Option[Int]): Either[String, Int] =
  ox.fold(Left("No number") : Either[String, Int])(x => Right(x))

Vous fournissez explicitement des informations de type sur le premier argument, qui peuvent à leur tour être utilisées pour déduire le paramètre type de fold. Vous aidez le mécanisme d'inférence de type.

D'un autre côté, vous pouvez simplement fournir explicitement le paramètre type pour fold comme ceci:

def foo(ox: Option[Int]): Either[String, Int] =
  ox.fold[Either[String, Int]](Left("No number"))(x => Right(x))

Maintenant, vos arguments réels (au niveau de la valeur) ne sont pas jonchés d'informations de type superflues, et il n'y a aucune inférence de type lorsque le compilateur le regarde, il peut dire immédiatement quel est le paramètre de type de fold, tel qu'il a été explicitement fourni. Utilisez les crochets pour spécifier explicitement le paramètre de type.

Un autre point, concernant x => Right(x) ici, vous créez pratiquement un nouveau littéral de fonction qui ne fait rien d'autre que passer x dans la méthode apply de l'objet compagnon de la classe Right case. Vous avez déjà une fonction de la forme appropriée disponible. Elle prend x et renvoie une Right(x). Il s'agit de la méthode apply. Vous pouvez vous y référer (le passer) directement.

def foo(ox: Option[Int]): Either[String, Int] =
  ox.fold[Either[String, Int]](Left("No number"))(Right.apply)
9
Peter Perháč