[关闭]
@runzhliu 2018-05-30T12:14:19.000000Z 字数 2942 阅读 1008

Scala Either, Left And Right

scala either left right


转载整理 http://colobu.com/2015/06/11/Scala-Either-Left-And-Right/

Scala 中的 Either 是一个有趣的类,它代表两个可能的类型的其中一个类型的值。这两个值之间没有交集。
Either 是抽象类,有两个具体的实现类: LeftRight

  1. sealed abstract class Either[+A, +B] {
  2. /**
  3. * Projects this `Either` as a `Left`.
  4. */
  5. def left = Either.LeftProjection(this)
  6. /**
  7. * Projects this `Either` as a `Right`.
  8. */
  9. def right = Either.RightProjection(this)
  10. /**
  11. * Applies `fa` if this is a `Left` or `fb` if this is a `Right`.
  12. *
  13. * @example {{{
  14. * val result: Either[Exception, Value] = possiblyFailingOperation()
  15. * log(result.fold(
  16. * ex => "Operation failed with " + ex,
  17. * v => "Operation produced value: " + v
  18. * ))
  19. * }}}
  20. *
  21. * @param fa the function to apply if this is a `Left`
  22. * @param fb the function to apply if this is a `Right`
  23. * @return the results of applying the function
  24. */
  25. def fold[X](fa: A => X, fb: B => X) = this match {
  26. case Left(a) => fa(a)
  27. case Right(b) => fb(b)
  28. }

Either 可以作为 scala.Option 的替换,可以用 scala.util.Left 替换 scala.None,用 scala.Right 替换 scala.Some

一般在时实践中使用 scala.util.Left 作为 Failure,而 scala.util.Right 作为成功的值。另一个类似的类是 scala.util.Try,举个例子:

  1. val in = Console.readLine("Type Either a string or an Int: ")
  2. val result: Either[String,Int] = try {
  3. Right(in.toInt)
  4. } catch {
  5. case e: Exception =>
  6. Left(in)
  7. }
  8. println( result match {
  9. case Right(x) => "You passed me the Int: " + x + ", which I will increment. " + x + " + 1 = " + (x+1)
  10. case Left(x) => "You passed me the String: " + x
  11. })

如果输入的值可以转换成 Int 类型,则结果 result 为 Right(int),否则 resultLeft(String)。你可以使用 match 处理结果。Either 类型还提供了投影(projection)操作。根据类型是 Left 还是 Right 返回不同的结果。

例如,使用一个函数转换 Either 类型的值时,如果值是 Left,应用 left 投影,然后调用 map 返回处理的结果,而应用 right 投影只是返回 Left,如:

  1. val l: Either[String, Int] = Left("flower")
  2. val r: Either[String, Int] = Right(12)
  3. l.left.map(_.size): Either[Int, Int] // Left(6)
  4. l.right.map(_.toDouble): Either[String, Double] // Left("flower")

同样地,对 Right 值进行 left 投影,只是返回 Right,不做改变,只会对 right 投影起作用:

  1. val l: Either[String, Int] = Left("flower")
  2. val r: Either[String, Int] = Right(12)
  3. r.left.map(_.size): Either[Int, Int] // Right(12)
  4. r.right.map(_.toDouble): Either[String, Double] // Right(12.0)

既然定义了 map 操作,就可以使用 for 语句:

  1. for (s <- l.left) yield s.size // Left(6)

Either 除了 left,right 投影外,还提供一些其它的方法:

  1. fold: l.fold(x => x.size, y => y.toDouble);r.fold(x => x.size, y => y.toDouble)
  2. swap: l.swap //Right(flower)
  3. merge: l.merge == "flower"
  4. joinLeft: LeftEither[Int, String], String).joinLeft
  5. joinRight: Either[A, Either[A, C]]

在 Scala 中使用 Future,经常会处理 Future 的最终结果,要不成功,要不失败,这里就使用了 Either

  1. val f: Future[List[String]] = future {
  2. session.getRecentPosts
  3. }
  4. f onComplete {
  5. case Right(posts) => for (post <- posts) render(post)
  6. case Left(t) => render("An error has occured: " + t.getMessage)
  7. }

前面提到 Try 也是两个值,SuccessFailure,它可以和 Either 进行转换:

  1. object Helper{
  2. implicit class EitherPimp[L <: Throwable,R](e:Either[L,R]){
  3. def toTry:Try[R] = e.fold(Failure(_), Success(_))
  4. }
  5. implicit class TryPimp[T](t:Try[T]){
  6. def toEither:Either[Throwable,T] = t.transform(s => Success(Right(s)), f => Success(Left(f))).get
  7. }
  8. }

或者

  1. import scala.util.{ Either, Failure, Left, Right, Success, Try }
  2. implicit def eitherToTry[A <: Exception, B](either: Either[A, B]): Try[B] = {
  3. either match {
  4. case Right(obj) => Success(obj)
  5. case Left(err) => Failure(err)
  6. }
  7. }
  8. implicit def tryToEither[A](obj: Try[A]): Either[Throwable, A] = {
  9. obj match {
  10. case Success(something) => Right(something)
  11. case Failure(err) => Left(err)
  12. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注