Home > Net >  Is there a way to chain `Try` as a monad while also not catching exceptions raised along the chain?
Is there a way to chain `Try` as a monad while also not catching exceptions raised along the chain?

Time:04-22

The Scala Try construct together with its flatMap does not work as I would expect or want it to. The TL;DR is that I want to do a series of operations that can fail in two ways: either by raising an exception, which should be promoted and caught higher up in the call stack, or by returning Failure, as the failure must logically be handled in different parts of the program.

I would expect something like this to do the trick:

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 _)

(Full Scastie with example)

However, in this case, the flatMap() call actually implicitly catches the uncaught exception thrown by secondStepThrows, which is not what I want (which is why I left out the Try block). Is there a way to get the same behaviour without the implicit exception-catching?

CodePudding user response:

Try.flatMap() did not caught exceptions implicitely, it is the essence of Try. When you use it, it is very explicit, and that's the goal.

I don't really understand what you want, but is something like that is possible for you ?

try {
  val first = firstStepSucceeds()
  val second = first.map(secondStepThrows).get
  val third = secondStepFails(second)
  // ...
}
catch {
  case e: Exception => ???
}

CodePudding user response:

I did some further experimentation, and what I ended up with was this reimplementation of Try as (the now right-biased and hence 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 _)
  • Related