@runzhliu
2018-05-30T12:14:19.000000Z
字数 2942
阅读 1125
scala
either
left
right
转载整理 http://colobu.com/2015/06/11/Scala-Either-Left-And-Right/
Scala 中的 Either
是一个有趣的类,它代表两个可能的类型的其中一个类型的值。这两个值之间没有交集。
Either
是抽象类,有两个具体的实现类: Left
和 Right
。
sealed abstract class Either[+A, +B] {
/**
* Projects this `Either` as a `Left`.
*/
def left = Either.LeftProjection(this)
/**
* Projects this `Either` as a `Right`.
*/
def right = Either.RightProjection(this)
/**
* Applies `fa` if this is a `Left` or `fb` if this is a `Right`.
*
* @example {{{
* val result: Either[Exception, Value] = possiblyFailingOperation()
* log(result.fold(
* ex => "Operation failed with " + ex,
* v => "Operation produced value: " + v
* ))
* }}}
*
* @param fa the function to apply if this is a `Left`
* @param fb the function to apply if this is a `Right`
* @return the results of applying the function
*/
def fold[X](fa: A => X, fb: B => X) = this match {
case Left(a) => fa(a)
case Right(b) => fb(b)
}
Either
可以作为 scala.Option
的替换,可以用 scala.util.Left
替换 scala.None
,用 scala.Right
替换 scala.Some
。
一般在时实践中使用 scala.util.Left
作为 Failure
,而 scala.util.Right
作为成功的值。另一个类似的类是 scala.util.Try
,举个例子:
val in = Console.readLine("Type Either a string or an Int: ")
val result: Either[String,Int] = try {
Right(in.toInt)
} catch {
case e: Exception =>
Left(in)
}
println( result match {
case Right(x) => "You passed me the Int: " + x + ", which I will increment. " + x + " + 1 = " + (x+1)
case Left(x) => "You passed me the String: " + x
})
如果输入的值可以转换成 Int
类型,则结果 result 为 Right(int)
,否则 result
为 Left(String)
。你可以使用 match
处理结果。Either
类型还提供了投影(projection)操作。根据类型是 Left
还是 Right
返回不同的结果。
例如,使用一个函数转换 Either
类型的值时,如果值是 Left
,应用 left
投影,然后调用 map
返回处理的结果,而应用 right
投影只是返回 Left
,如:
val l: Either[String, Int] = Left("flower")
val r: Either[String, Int] = Right(12)
l.left.map(_.size): Either[Int, Int] // Left(6)
l.right.map(_.toDouble): Either[String, Double] // Left("flower")
同样地,对 Right
值进行 left 投影,只是返回 Right
,不做改变,只会对 right 投影起作用:
val l: Either[String, Int] = Left("flower")
val r: Either[String, Int] = Right(12)
r.left.map(_.size): Either[Int, Int] // Right(12)
r.right.map(_.toDouble): Either[String, Double] // Right(12.0)
既然定义了 map
操作,就可以使用 for
语句:
for (s <- l.left) yield s.size // Left(6)
Either
除了 left,right 投影外,还提供一些其它的方法:
fold: l.fold(x => x.size, y => y.toDouble);r.fold(x => x.size, y => y.toDouble)
swap: l.swap //Right(flower)
merge: l.merge == "flower"
joinLeft: LeftEither[Int, String], String).joinLeft
joinRight: Either[A, Either[A, C]]
在 Scala 中使用 Future
,经常会处理 Future
的最终结果,要不成功,要不失败,这里就使用了 Either
:
val f: Future[List[String]] = future {
session.getRecentPosts
}
f onComplete {
case Right(posts) => for (post <- posts) render(post)
case Left(t) => render("An error has occured: " + t.getMessage)
}
前面提到 Try
也是两个值,Success
和 Failure
,它可以和 Either
进行转换:
object Helper{
implicit class EitherPimp[L <: Throwable,R](e:Either[L,R]){
def toTry:Try[R] = e.fold(Failure(_), Success(_))
}
implicit class TryPimp[T](t:Try[T]){
def toEither:Either[Throwable,T] = t.transform(s => Success(Right(s)), f => Success(Left(f))).get
}
}
或者
import scala.util.{ Either, Failure, Left, Right, Success, Try }
implicit def eitherToTry[A <: Exception, B](either: Either[A, B]): Try[B] = {
either match {
case Right(obj) => Success(obj)
case Left(err) => Failure(err)
}
}
implicit def tryToEither[A](obj: Try[A]): Either[Throwable, A] = {
obj match {
case Success(something) => Right(something)
case Failure(err) => Left(err)
}