Ben*_*ich 16
好处Either
是你可以记录缺少的原因.例如,如果您正在使用Options
,您可能会遇到这样的情况:
val xOpt = Option(1)
val yOpt = Option(2)
val zOpt = None
val tupled = for {
x <- xOpt
y <- yOpt
z <- zOpt
} yield (x, y, z)
Run Code Online (Sandbox Code Playgroud)
现在,如果tupled
是None
,我们真的不知道为什么!如果这是其余行为的重要细节,使用Either
可以帮助:
val tupled = for {
x <- xOpt.toRight("x is missing").right
y <- yOpt.toRight("y is missing").right
z <- zOpt.toRight("z is missing").right
} yield (x, y, z)
Run Code Online (Sandbox Code Playgroud)
这将返回Left(msg)
消息是第一个缺失值的相应消息的位置,或者返回Right(value)
为tupled值.通常保持Left
用于失败和Right
成功.
当然,您也可以Either
更广泛地使用,不仅在缺少值或特殊值的情况下.还有其他情况Either
可以帮助表达简单联合类型的语义.
使用特殊值的第三个常用习语是Try
monad:
val xTry = Try("1".toInt)
val yTry = Try("2".toInt)
val zTry = Try("asdf".toInt)
val tupled = for {
x <- xTry
y <- yTry
z <- zTry
} yield (x, y, z)
Run Code Online (Sandbox Code Playgroud)
Try[A]
是同构的Either[Throwable, A]
.换句话说,您可以将a Try
视为Either
左类型Throwable
,并且可以将任何Either
左类型Throwable
视为a Try
.也是Option[A]
同形的Try[A]
.因此,您可以将a Option
视为Try
忽略错误.因此,您也可以将其视为一种传承Either
.实际上,标准库支持其中一些转换:
//Either to Option
Left[Int, String](1).left.toOption //Some(1)
Right[Int, String]("foo").left.toOption //None
//Try to Option
Try("1".toInt).toOption //Some(1)
Try("foo".toInt).toOption //None
//Option to Either
Some(1).toRight("foo") //Right[String, Int](1)
(None: Option[Int]).toRight("foo") //Left[String, Int]("foo")
Run Code Online (Sandbox Code Playgroud)
标准库不包括从转换Either
到Try
,从Try
到Either
,或者Option
到Try
.但是,根据需要,丰富Option
,非常简单:Try
Either
object OptionTryEitherConversions {
implicit class EitherToTry[L <: Throwable, R](val e: Either[L, R]) extends AnyVal {
def toTry: Try[R] = e.fold(Failure(_), Success(_))
}
implicit class TryToEither[T](val t: Try[T]) extends AnyVal {
def toEither: Either[Throwable, T] = t.map(Right(_)).recover(PartialFunction(Left(_))).get
}
implicit class OptionToTry[T](val o: Option[T]) extends AnyVal {
def toTry(throwable: Throwable): Try[T] = o.map(Right(_)).getOrElse(Left(throwable))
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许您:
import OptionTryEitherConversions._
//Try to Either
Try(1).toEither //Either[Throwable, Int] = Right(1)
Try("foo".toInt).toEither //Either[Throwable, Int] = Left(java.lang.NumberFormatException)
//Either to Try
Right[Throwable, Int](1).toTry //Success(1)
Left[Throwable, Int](new Exception).toTry //Failure(java.lang.Exception)
//Option to Try
Some(1).toTry(new Exception) //Success(1)
(None: Option[Int]).toTry(new Exception) //Failure(java.lang.Exception)
Run Code Online (Sandbox Code Playgroud)
Either
可以看作是一种概括Option
.如果您修复了第一个值Either
,例如通过将其设置为Unit
,则可以获得与以下内容基本相同的方式Option
:
Option[X] := Either[Unit, X]
Run Code Online (Sandbox Code Playgroud)
在这种情况下,Left[Unit, X]
将对应于None
并且Right[Unit, X]
将对应于Some[X]
.
因为Option[X]
,None
会发出某种失败信号,以获得类型的价值X
,并Some[X]
表示成功.因为Either[Unit, X]
,类型的实例Left[Unit, X]
将代表失败,并Right[Unit, X]
代表成功.
但是,您可以使用第一个组件Either
来存储有关失败原因的更详细信息,或者一些可帮助您从错误中恢复的其他信息.Option
给你一个None
,这不是很有用.但是Either[F,X]
可以返回一个成功值Right[F, X]
,它实际上只是一个包装器X
,或者是Left[F, X]
一个F
表示失败的类型值的故障的详细描述.
这允许您定义更复杂的恢复策略.例如,看看Form.scala
Play!-Framework.他们使用Either
到处都是,因为他们要么响应用户的表单提交,要么发回一个部分填写的表单,注释有用的错误消息.替代方法是使用Option[TypeOfFormContent]
,None
如果某些表单字段包含无效输入,则会评估.这反过来意味着用户会得到类似"错误请求.请重新填写整个表单"的内容.作为回应,这将是非常烦人的.因此,Either
使用而不是Option
,因为它实际上可以跟踪表单提交到底出了什么问题.
缺点Either
是它不是monad:为了有效地使用它,你总是必须为两个不同的情况传递两个不同的回调.这可能导致"回调 - 地狱".因此,应该仔细考虑对失败的确切描述是否有价值.在表单提交失败的情况下,故障的详细描述是有价值的,因为人们不想强迫用户再次重新键入所有内容.在其他情况下,Option
可能更合适,因为人们不想强迫程序员处理不可恢复错误的不必要的详细描述.
归档时间: |
|
查看次数: |
1805 次 |
最近记录: |