有没有办法将“Try”链接为 monad,同时又不捕获沿链引发的异常?
Is there a way to chain `Try` as a monad while also not catching exceptions raised along the chain?
Scala Try
构造及其 flatMap
没有像我期望或希望的那样工作。 TL;DR 是我想做一系列可能以两种方式失败的操作:要么通过引发异常,该异常应该被提升并在调用堆栈的更高位置捕获,要么通过返回 Failure
,如逻辑上必须在程序的不同部分处理失败。
我希望这样的事情可以解决问题:
def firstStepSucceeds(): Try[Int] = Try {
1
}
def secondStepThrows(input: Int) = {
throw new Exception("Exception thrown in second step")
}
// I expect this to propagate the exception thrown in secondStepThrows
firstStepSucceeds() flatMap (secondStepThrows _)
然而,在这种情况下,flatMap()
调用实际上隐式捕获了 secondStepThrows
抛出的未捕获异常,这不是我想要的(这就是为什么我遗漏了 Try
堵塞)。有没有办法在没有隐式异常捕获的情况下获得相同的行为?
Try.flatMap() 并没有隐含地捕获异常,这是Try的本质。
当你使用它时,它非常明确,这就是目标。
我不是很明白你想要什么,但这样的事情对你来说可能吗?
try {
val first = firstStepSucceeds()
val second = first.map(secondStepThrows).get
val third = secondStepFails(second)
// ...
}
catch {
case e: Exception => ???
}
我做了一些进一步的实验,我最终得到的是 Try
的重新实现为(现在的 right-biased 因此是 monadic)Either
:
object CatchAll {
def apply[SomeType](block: => SomeType) = try { Right(block) }
catch { case e: Throwable => Left(e) }
}
def firstStepSucceeds() = CatchAll {
1
}
def firstStepFails() = CatchAll {
throw new Exception("First step failed")
}
def secondStepSucceeds(input: Int) = CatchAll {
input + 1
}
def secondStepFails(input: Int) = CatchAll {
throw new Exception("Second step failed in try block!")
}
def secondStepThrows(input: Int) = {
throw new Exception("Second step failed unexpectedly!")
}
firstStepSucceeds() flatMap (secondStepSucceeds _)
firstStepFails() flatMap (secondStepSucceeds _)
firstStepSucceeds() flatMap (secondStepFails _)
// This now throws an exception as expected
//firstStepSucceeds() flatMap (secondStepThrows _)
Try
中发生的事情应该保留在 Try
中。如果返回 Try
的函数有时也会抛出异常,大多数 Scala 程序员会 非常 感到惊讶。
如果你想在不同的地方处理异常,典型的模式是通过异常的类型来区分。所以
val partiallyRecoveredTry = originalTry.recover{
case _: SecondStepException => "second step had an exception"
}
// Further up the call stack
partiallyRecoveredTry.getOrElse("first step had an exception")
Scala Try
构造及其 flatMap
没有像我期望或希望的那样工作。 TL;DR 是我想做一系列可能以两种方式失败的操作:要么通过引发异常,该异常应该被提升并在调用堆栈的更高位置捕获,要么通过返回 Failure
,如逻辑上必须在程序的不同部分处理失败。
我希望这样的事情可以解决问题:
def firstStepSucceeds(): Try[Int] = Try {
1
}
def secondStepThrows(input: Int) = {
throw new Exception("Exception thrown in second step")
}
// I expect this to propagate the exception thrown in secondStepThrows
firstStepSucceeds() flatMap (secondStepThrows _)
然而,在这种情况下,flatMap()
调用实际上隐式捕获了 secondStepThrows
抛出的未捕获异常,这不是我想要的(这就是为什么我遗漏了 Try
堵塞)。有没有办法在没有隐式异常捕获的情况下获得相同的行为?
Try.flatMap() 并没有隐含地捕获异常,这是Try的本质。 当你使用它时,它非常明确,这就是目标。
我不是很明白你想要什么,但这样的事情对你来说可能吗?
try {
val first = firstStepSucceeds()
val second = first.map(secondStepThrows).get
val third = secondStepFails(second)
// ...
}
catch {
case e: Exception => ???
}
我做了一些进一步的实验,我最终得到的是 Try
的重新实现为(现在的 right-biased 因此是 monadic)Either
:
object CatchAll {
def apply[SomeType](block: => SomeType) = try { Right(block) }
catch { case e: Throwable => Left(e) }
}
def firstStepSucceeds() = CatchAll {
1
}
def firstStepFails() = CatchAll {
throw new Exception("First step failed")
}
def secondStepSucceeds(input: Int) = CatchAll {
input + 1
}
def secondStepFails(input: Int) = CatchAll {
throw new Exception("Second step failed in try block!")
}
def secondStepThrows(input: Int) = {
throw new Exception("Second step failed unexpectedly!")
}
firstStepSucceeds() flatMap (secondStepSucceeds _)
firstStepFails() flatMap (secondStepSucceeds _)
firstStepSucceeds() flatMap (secondStepFails _)
// This now throws an exception as expected
//firstStepSucceeds() flatMap (secondStepThrows _)
Try
中发生的事情应该保留在 Try
中。如果返回 Try
的函数有时也会抛出异常,大多数 Scala 程序员会 非常 感到惊讶。
如果你想在不同的地方处理异常,典型的模式是通过异常的类型来区分。所以
val partiallyRecoveredTry = originalTry.recover{
case _: SecondStepException => "second step had an exception"
}
// Further up the call stack
partiallyRecoveredTry.getOrElse("first step had an exception")