Quick failure when using for-comprehension with scala.util.Try - scala

I really like scala.util.Try in Scala 2.10, and how it works with for-comprehension makes handling multiple steps that could go wrong easily.
For example, we could use the following code to make sure we only print out that two numbers if and only if everything is under control and we get value correctly.
def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}
for {
a <- tryA
b <- tryB
} {
println (s"We got:${a+b}")
}
But one of my concern is that this code is actually ignore any exceptions, which means it will looks like the following try-cactch block:
try {
// .....
} catch {
case _: Exception => // Swallow any exception
}
As far as I know, there is an argument that this kind of codes is a bad smell, because no one will notice there is an exception occurs.
What I would like to achieve is that still using for to make sure the println only execute if everything is OK, but if there is any exception in any steps, it will blow up and throw out the exception directly.
Currently here is how I do this, but it seems less elegant because it introduce a new Try[Unit] object, so I'm wondering how could I make this code better?
For example, is it possible to get rid of the result variable and result.get statement, but still get exception being thrown?
def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}
val result = for {
a <- tryA
b <- tryB
} yield {
println (s"We got:${a+b}")
}
result.get
Update
To make thing more clear, it is the result from Scala REPL of the first code in this question.
scala> def tryA: Try[Int] = Success(1)
tryA: scala.util.Try[Int]
scala> def tryB: Try[Int] = Failure(new Exception("error"))
tryB: scala.util.Try[Int]
scala> for {
| a <- tryA
| b <- tryB
| } {
| println (s"We got:${a+b}")
| }
scala>
We can see that nothing happens here, even tryB is a Failure with exception. What I would like to get is an exception being thrown, and without the introduce the new Try[Unit] object with yield, is this possible?

You can use recover:
import scala.util.Try
def tryEven = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i }
def tryEvenOrNeg1 = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i } recover { case exx: Exception => -1 }
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res1: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res2: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res3: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 542, -1
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res5: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res6: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 692, 750
I removed the resNN that reflected Success(()).

OK, I forgot we always has implicit conversion in Scala. ;-)
So we could implement this behaviour ourself, it will create more object than the yield version, but I think the intention of this code is much more clear.
implicit class BlowUpTry[T](current: Try[T]) {
def throwIfFailed: Try[T] = current match {
case Success(value) => current
case Failure(exception) => throw exception
}
}
def tryA: Try[Int] = Success(1)
def tryB: Try[Int] = Failure(new Exception("error"))
for {
a <- tryA.throwIfFailed
b <- tryB.throwIfFailed
} {
println(s"We got ${a + b}")
}

Related

Scala shortcut first solution in a for-comprehension list

I've got a coding draft which works so far as it delivers the correct answer. But from the esthetics side, it could be improved, my guess!
Aim: Find first solution in a list of many possible solutions. When found first solution, don't calculate further. In real-world application, the calculation of each solution/non-solution might be more complex for sure.
Don't like: The Solution=Left and NoSolution=Right aliasing is contra-intuitive, since Right normally stands for success and here Left and Right are swapped (since technically when using Either only Left shortcuts the for-comprehension list)
Is there a nice way to improve this implementation? or another solution?
package playground
object Test {
def main(args: Array[String]): Unit = {
test
}
val Solution = Left
val NoSolution = Right
def test: Unit = {
{
// Find the first solution in a list of computations and print it out
val result = for {
_ <- if (1 == 2) Solution("impossible") else NoSolution()
_ <- NoSolution()
_ <- NoSolution(3)
_ <- Solution("*** Solution 1 ***")
_ <- NoSolution("oh no")
_ <- Solution("*** Solution 2 ***")
x <- NoSolution("no, no")
} yield x
if (result.isLeft)
println(result.merge) // Prints: *** Solution 1 ***
}
}
}
So you're looking for something that's "monaduck": i.e. has flatMap/map but doesn't necessarily obey any monadic laws (Scala doesn't even require that flatMap have monadic shape: the chain after desugaring just has to typecheck); cf. duck-typing.
trait Trial[+Result] {
def result: Option[Result]
def flatMap[R >: Result](f: Unit => Trial[R]): Trial[R]
def map[R](f: Result => R): Trial[R]
}
case object NoSolution extends Trial[Nothing] {
def result = None
def flatMap[R](f: Unit => Trial[R]): Trial[R] = f(())
def map[R](f: Result => R): Trial[R] = this
}
case class Solution[Result](value: Result) extends Trial[Result] {
def result = Some(value)
def flatMap[R >: Result](f: Unit => Trial[R]): Trial[R] = this
def map[R](f: Result => R): Trial[R] = Solution(f(value))
}
scala> for {
| _ <- if (1 == 2) Solution("nope") else NoSolution
| _ <- NoSolution
| _ <- Solution("yay!")
| _ <- NoSolution
| x <- Solution("nope")
| } yield x
res0: Trial[String] = Solution(yay!)
scala> for {
| _ <- if (1 == 2) Solution("nope") else NoSolution
| _ <- NoSolution
| _ <- Solution("yay!")
| x <- NoSolution
| } yield x
res1: Trial[String] = Solution(yay!)
scala> for {
| _ <- if (1 == 2) Solution("nope") else NoSolution
| x <- NoSolution
| } yield x
res2: Trial[String] = NoSolution
Clearly, monadic laws are being violated: the only thing we could use for pure is Solution, but
scala> val f: Unit => Trial[Any] = { _ => NoSolution }
f: Unit => Trial[Any] = $Lambda$107382/0x00000008433be840#6c0e35d7
scala> Solution(5).flatMap(f)
res7: Trial[Any] = Solution(5)
scala> f(5)
<console>:13: warning: a pure expression does nothing in statement position
f(5)
^
res8: Trial[Any] = NoSolution
Absent Scala's willingness to convert any pure value to Unit, that wouldn't even type check, but still, it breaks left identity.

How to connect two Scala Futures

I have two Future functions:
def parseIntFuture(str: String) = Future{scala.util.Try(str.toInt).toOption}
def divideFuture(a: Int, b: Int) = Future{ if (b == 0) None else Some(a / b)}
And now I want connect them and eventually get a Future[Option[Int]] type result which is the second one's return value, but if I do like this:
def stringDivideBy(aStr: String, bStr: String) = {
val x = for {
aNum <- parseIntFuture(aStr)
bNum <- parseIntFuture(bStr)
} yield (aNum, bNum)
x.map(n => {
for{
a <- n._1
b <- n._2
} yield divideFuture(a, b)
})
}
Actually I will get Future[Option[Future[Option[Int]]]] instead of Future[Option[Int]] only. I know it's because I'm passing one Future to the other, but I don't know what is the correct way to connect these two Futures one by one avoiding using Await. I halt explicitly use Await, then what would be the solution?
You don't need monad transformers and other "heavy artillery" for simple stuff like this. The general rule is don't make your code more complex than it absolutely has to be.
(parseIntFuture(foo) zip parseIntFuture(bar))
.flatMap {
case (Some(a), Some(b)) => divideFuture(a, b)
case _ => Future.successful(None)
}
There is this thing called OptionT monad transformer that solves exactly this problem. With OptionT, your code would look somewhat like
import cats.data.OptionT
// ...
val x = (for {
aNum <- OptionT(parseIntFuture(aStr))
bNum <- OptionT(parseIntFuture(bStr))
res <- OptionT(divideFuture(aNum, bNum))
} yield res).value
and return a Future[Option[Int]].
You could avoid monad transformers at the cost of nested for-comprehensions:
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
def parseIntFuture(str: String) = Future{scala.util.Try(str.toInt).toOption}
def divideFuture(a: Int, b: Int) = Future{ if (b == 0) None else Some(a / b)}
def stringDivideBy(aStr: String, bStr: String): Future[Option[Int]] = {
for {
aOpt <- parseIntFuture(aStr)
bOpt <- parseIntFuture(bStr)
resOpt <-
(for {
a <- aOpt
b <- bOpt
} yield divideFuture(a, b))
.getOrElse(Future { None })
} yield resOpt
}

Scala-Not compiling for comprehension

I am trying to run the following code:
def split(input: Int): List[Int] = {
val inputAsString = input.toString
val inputAsStringList = inputAsString.split("").toList
inputAsStringList.map(_.toInt).reverse
}
split(3122)
def increment(list: List[Int]): List[Int] = {
def loop(multiplier: Int, result: List[Int], list: List[Int]): List[Int] = list match {
case x :: xs =>
val newList = (x * multiplier) :: result
loop(multiplier * 10, newList, xs)
case Nil => result
}
loop(1, List(), list)
}
val result: List[Int] = for {
splited <- split(3122)
incremented <- increment(splited)
} yield incremented
But the line incremented <- increment(splited) is giving the following error:
Type mismatch, expected: List[Int], actual: Int
Why is this happening if both functions are returning the same data type?
Your increment function takes a List[Int], but splited is an Int while in the for comprehension. This is because at the line splited <- split(3122), you are really saying for every x: Int in split(y): List[Int]. If you want it to compile, you want your val result code to look like this:
...
val splited = split(3122)
val result: List[Int] = for {
incremented <- increment(splited)
} yield incremented
This returns result: List[Int] = List(2). Whether you expect this or not is another thing - I'm not sure what you expect increment to return.
Please consider the following example to understand what for-comprehension does:
//For comprehension:
for {
a <- aMonad
b <- bMonad
//....some more....
z <- zMonad
} yield {
???
}
//means:
aMonad.flatMap { a =>
bMonad.flatMap { b =>
//{{{...some more
//notice here it is .map instead of flatMap
zMonad.map { z =>
//something
???
}
//}}}
}
}
The monads aMonad, bMonad, .. zMonad should be monads of similar types, like, List[_], Future[_] etc to be used in a for-comprehension.
That means, aMonad of type List[Int] and bMonad of type Future[Int] cannot be used in a for-comprehension like in the above code, while there is no problem if they are of List[Int] and List[String] type.
Further readings:
https://docs.scala-lang.org/tutorials/FAQ/yield.html
https://darrenjw.wordpress.com/2016/04/15/first-steps-with-monads-in-scala/

Scala - concatenate number of Try() results

I have number of interfaces where each one of the returns Try() result.
For example:
def getElements1(id: Guid): Try[Seq[SpecialElement]] //From interface A
def getElements2(id: Guid): Try[Seq[SpecialElement]] //From interface B
def getElements3(id: Guid): Try[Seq[SpecialElement]] //From interface C
The all independent and can fail randomly.
What is the nicest 'Scala way' to concatenate their output with respect to Failure() case as well?
Well... nicest 'Scala way' depends on what your requirements are ?
So... you have following 3 defs,
def getElements1(id: Guid): Try[Seq[SE]]
def getElements2(id: Guid): Try[Seq[SE]]
def getElements3(id: Guid): Try[Seq[SE]]
Case 1 - The result fails, if at least 1 of them fails and you get the error of only 1 failure.
val result: Try[Seq[SE]] = for {
emements1 <- getElements1(id)
emements2 <- getElements2(id)
emements3 <- getElements3(id)
} yield emements1 ++ emements2 ++ emements3
Case 2 - The result fails, if at least 1 of them fails and you want to get the errors of all failures,
def trySeqToEither[T](tryTSeq: Seq[Try[T]]): Either[Seq[Throwable], Seq[T]] = {
val accInit: Either[Seq[Throwable], Seq[T]] = Right(Seq.empty[T])
tryTSeq.aggregate(accInit)({
case (Right(seq), Success(t)) => Right(seq :+ t)
case (Right(seq), Failure(ex)) => Left(Seq[Throwable](ex))
case (Left(seq), Success(t)) => Left(seq)
case (Left(seq), Failure(ex)) => Left(seq :+ ex)
})
}
val seqResultEither: Either[Seq[Throwable], Seq[Seq[SE]]] = trySeqToEither(
Seq(getElements1(id), getElements2(id), getElements3(id))
)
val resultEither: Either[Seq[Throwable], Seq[SE]] = seqResultEither match {
case Right(seqResult) => Right(seqResult.flatten)
case Left(seqThrowable) => Left(seqThrowable)
}
Case 3 - The result ignores the failed computations
val emementsOption1 = getElements1(id).toOption
val emementsOption1 = getElements2(id).toOption
val emementsOption3 = getElements3(id).toOption
val result: Seq[SE] = Seq[Seq[SE]](emementsOption1, emementsOption2, emementsOption3).flatten
You can use for comprehensions:
case class SpecialElement(x: Int)
val x: Try[Seq[SpecialElement]] = Try(List(SpecialElement(1)))
val y: Try[Seq[SpecialElement]] = Try(List(SpecialElement(2)))
val z: Try[Seq[SpecialElement]] = Try(List(SpecialElement(3)))
for {
a <- x
b <- y
c <- z
} yield a ++ b ++ c
Success(List(SpecialElement(1), SpecialElement(2), SpecialElement(3)))
for {
a <- x
b <- y
c <- Try(throw new Exception)
} yield a ++ b ++ c
Failure(java.lang.Exception)

Nested For Comprehensions with Futures

Given:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def f: Future[Either[String, Int]] = Future { Right(100)}
def plus10(x: Int): Future[Either[String, Int]] =
Future { Right(x + 10) }
I'm trying to chain the Future[...] together as so:
scala> for {
| x <- f
| y <- for { a <- x.right } yield plus10(a)
| } yield y
<console>:17: error: value map is not a member of Product with
Serializable with
scala.util.Either[String,scala.concurrent.Future[Either[String,Int]]]
y <- for { a <- x.right } yield plus10(a)
^
I am expecting to get: Future{Right(100)} as a result, but I get the above compile-time error.
Travis Brown gave an excellent answer on how to use Monad Transformers to fix my code here. However, how can I fix my code without Monad Transformers?
Turns out that I can use Either#fold:
scala> for {
| a <- f
| b <- a.fold(_ => Future { Left("bad") }, xx => plus10(xx) )
| } yield b
res16: scala.concurrent.Future[Either[String,Int]] =
scala.concurrent.impl.Promise$DefaultPromise#67fc2aad
scala> res16.value
res17: Option[scala.util.Try[Either[String,Int]]] =
Some(Success(Right(110)))
I was about to answer when yours appeared, but you might still look at this:
val res = for {
x <- f
y <- x.fold(x => Future{Left(x)}, plus10)
} yield y
It is a little more concise on the right side and keeps the left side.