What is the proper way to return from an exception in Scala? - scala

In a non-functional language, I might do something like:
try {
// some stuff
} catch Exception ex {
return false;
}
// Do more stuff
return true;
In Scala, however, this pattern is clearly not correct. If my scala code looks like this:
try {
// do some stuff
}
catch {
case e: Exception => // I want to get out of here and return false
)
}
// do more stuff
true
How do I properly do that? I don't want to use the "return" statement, of course, but I also don't want to drop through and "do more stuff" and eventually return true.

You want to represent a computation that can either succeed or signal that an error has occurred. That's the perfect use case for the Try monad.
import scala.util.{ Try, Success, Failure }
def myMethod: Try[Something] = Try {
// do stuff
// do more stuff
// if any exception occurs here, it gets wrapped into a Failure(e)
}
So you're returning a Try instead of a Bool, which is infinitely more clear and idiomatic.
Usage example:
myMethod match {
case Success(x) => println(s"computation succeded with result $x")
case Failure(e) => println(s"computation failed with exception $e.getMessage")
}
If you don't even care about the exception, but you just want to return a value in case of success, you can even convert the Try to an Option.
def myMethod: Option[Something] = Try {
// do stuff
// do more stuff
// return something
// if any exception occurs here, it gets wrapped into a Failure(e)
}.toOption
myMethod match {
case Some(x) => println(s"computation succeded with result $x")
case None => println("computation failed")
}
To respond to the question in the comments, you can do
Try {
// do stuff
} match {
case Failure(_) => false
case Success(_) =>
// do more stuff
// true
}
although I would suggest to return something more meaningful than a Boolean, whenever it makes sense.
Of course this can be nested
Try {
// do stuff
} match {
case Failure(_) => false
case Success(_) =>
// do more stuff
Try {
// something that can throw
} match {
case Failure(_) => false
case Success(_) =>
// do more stuff
true
}
}
but you should consider putting the Try chunks into separate functions (returning a Try).
Ultimately, we can take advantage of the fact that Try is a monad, and do something like this
Try { /* java code */ }.flatMap { _ =>
// do more stuff
Try { /* java code */ }.flatMap { _ =>
// do more stuff
Try { /* java code */ }
}
} match {
case Failure(_) => false // in case any of the Try blocks has thrown an Exception
case Success(_) => true // everything went smooth
}

scala> def f() = try { ??? ; 1 } catch { case _: Throwable => 2 }
f: ()Int
scala> f()
res2: Int = 2
scala> import util._
import util._
scala> def g() = Try { ??? ; 1 } recover { case _ => 2 } get
warning: there was one feature warning; re-run with -feature for details
g: ()Int
scala> g()
res3: Int = 2
HTH. Small functions helps.
Another hint:
scala> def j() = Try (1) map (_ + 42) recover { case _ => 2 } get
warning: there was one feature warning; re-run with -feature for details
j: ()Int
scala> j()
res4: Int = 43

try-catch expression is not good for functional programming.
Anyway, an easy solution that still uses try-catch:
val result = try {
// do some stuff
Some(/* The final expression */)
}
catch {
case e: Exception => // Do something or nothing
None
}
result match {
case Some(r) => // Do something with r
true
case None => false
}
You can use scala.util.Try for cleaner and more functional-styled code.
Refer to https://codereview.stackexchange.com/questions/38689/code-with-many-early-returns-exits-into-the-functional-style
I faced a similar problem like you, but the answer in the Stackexchange CodeReview helped me a lot.

Successful or erroneous cases can be represented by several Scala type.
If you consider the error in the meaning of "something is missing" (e.g. file not found), you can use Option[T] (e.g. Option[File]), with case values None or Some(T). Then orElse, getOrElse or fold and map/flatMap functions can be used to dispatch cased.
You can also use Either[E, T], with (by convention), in Left(E) case the error value (e.g. a String as error message), and in Right(T) successful T value.
Monads Try[T] or Future[T] can be used in the same way.
In the field of I/O, the very nice scala-arm lib provide type ManagedResource[T] that wrap either successful (T) or erroneous (List[Throwable]]) result of computation based on resources.
Valudation types in Scalaz are also useful for such cases.

'util.Try{ do some stuff }.isSuccess'

Related

Propagate errors through a chain of Scala futures

Considering a sequence of futures each returning Either[Status, Resp].
How would you propagate error status codes through a for comprehension which is using Future and not Either?
The code bellow does not work, since the parsing exception is not caught by .recover of the last future
The use case is Scala Play ActionRefiners which returns Future[Either[Status, TRequest[A]]].
def parseId(id: String):Future[Int] = {
Future.successful(Integer.parseInt(id))
}
def getItem(id: Int)(implicit ec: ExecutionContext): Future[Either[Status, String]] =
Future(Some("dummy res from db " + id)).transformWith {
case Success(opt) => opt match {
case Some(item) => Future.successful(Right(item))
case _ => Future.successful(Left(NotFound))
}
case Failure(_) => Future.successful(Left(InternalServerError))
}
(for {
id <- parseId("bad request")
resp <- getItem(id)
} yield resp).recover {
case _:NumberFormatException => Left(BadRequest)
}
I could move the .recover to parseId, but this makes the for comprehension very ugly - having to treat the Either[Status, id] in the middle
def parseId(id: String):Future[Either[Status, Int]] = {
Future.successful(Right(Integer.parseInt(id))).recover {
case _:NumberFormatException => Left(BadRequest)
}
}
Your exception is not caught because you are not throwing it inside the Future: Future.successful is immediately satisfied with the result of the expression you give it, if it throws an exception, it is executed on the current thread.
Try removing the .successful: Future(id.toInt) will do what you want.
Also, I would recommend to get rid of all the Eithers: these are highly overrated/overused, especially in the context of Future (that already wrap their result into Try anyhow), and just make the code more complicated and less readable without offering much benefit.
case class FailureReason(status: Status)
extends Exception(status.toString)
def notFound() = throw FailureReason(NotFound)
def internalError() = throw FailureReason(InternalError)
def badRequest() = throw FailureReason(BadRequest)
def parseId(id: String):Future[Int] = Future(id.toInt)
def getItem(id: Int): Future[String] = Future(Some("dummy"))
.map { _.getOrElse(notFound) }
.recover { _ => internalError }
// this is the same as your for-comprehension, just looking less ugly imo :)
parseId("foo").flatMap(getItem).recover {
case _: NumberFormatException => badRequest()
}
// if you still want `Either` in the end for some reason:
.map(Right.apply[Status, String])
.recover {
case _: NumberFormatException => Left(BadRequest) // no need for the first recover above if you do this
case FailureReason(status) => Left(status)
}

Code runs with fastOptJS but fails with fullOptJS in Scala.js

I'm catching an UndefinedBehaviorError thrown by the bad asInstanceOf in my code like this:
$("button").on(
"click",
".showDialog",
(event: JQueryEventObject) => {
event.preventDefault()
val helpText = "Enter any number"
js.Dynamic.global.swal(
JC(
title = helpText,
text = helpText,
`type` = "input",
showCancelButton = true,
closeOnConfirm = false,
inputPlaceholder = helpText
),
(input: Any) => {
try {
input.asInstanceOf[Boolean]
} catch {
case _: Throwable =>
try {
val correctInput = input.asInstanceOf[String].toDouble
global.console.log("success")
} catch {
case _: Throwable =>
js.Dynamic.global.swal.showInputError("Incorrect data!")
}
}
}
)
}
)
It works fine with fastOptJS but fails with fullOptJS. How to correctly rewrite this code to run it with fullOptJS or what are the other options?
In Scala.js, a bad cast does not reliably throw a ClassCastException, unlike in Scala/JVM. Instead, a bad cast is an undefined behavior, as specified here. This means that in fullOpt, the program can behave arbitrarily, and in particular is allowed not in throw. In fastOpt, however, the compiler is kind and reliably throws, but not a ClassCastException (as it would give a false sense of security), rather an UndefinedBehaviorError.
UndefinedBehaviorErrors must never be caught, as catching one hides a bug which will surface in fullOpt. This is your mistake here: you are catching that UndefinedBehaviorError under case _: Throwable =>. Note that catching Throwable is bad practice in Scala. Instead, you should be using case scala.util.control.NonFatal(_) =>. A NonFatal handler does not catch UndefinedBehaviorError, and would therefore not hide the bug.
Now to solve your issue, you simply not use asInstanceOf as a means to type-test. Even on Scala/JVM, that code would be super bad practice. Use pattern matching instead:
(input: Any) => {
def reportIncorrectData(): Unit =
js.Dynamic.global.swal.showInputError("Incorrect data!")
input match {
case input: Boolean =>
// Here, input is a Boolean
input
case input: String =>
// Here, it is a String; let's try to convert it to Double
try {
val correctInput = input.toDouble
} catch {
case _: NumberFormatException =>
reportIncorrectData()
}
case _ =>
// It is something else, always incorrect data
reportIncorrectData()
}
}

How to avoid 'Non-variable type argument is unchecked since it is eliminated by erasure'?

A project I am working on has the following piece code:
val ftr1: Future[Try[(Date, String)]] = Future {
if (someCondition) {
// some code
val amazonClient = Try[new com.amazonaws.services.s3.AmazonS3Client(...)]
amazonClient.map { c =>
// doing some stuff
(new Date, "SomeString")
}
} else {
// some code
Failure(new Exception)
}
}
Future.firstCompletedOf(Seq(ftr1, anotherFuture)) map {
case t: Try[(Date, String)] => {
t match {
case Success(r) => //do some things
case _ => //do some another things
}
case _ => //do some another things
}
So, during compiling I have following warning:
[warn] non-variable type argument java.util.Date in type pattern java.util.Date, String) is unchecked since it is eliminated by erasure
and
[warn] case t: (Date, String) => //do some things
And I actually do not understand, what these warnings mean and how can this code be refactored in order to get rid of these warnings?
Try[+T] is an abstract class, one which you can't create an instance of.
There are two case classes which inherit from it, Success and Failure, which you should really be using.
The compiler is warning you that at compile time, type erasure is going to erase those types, thus matching on them isn't going to get you the results you want. More on that can be found in How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?
But, you can avoid all this completely if you simply match on Success and Failure to reduce the unnecessary nesting in your match:
val ftr1: Future[Try[(Date, String)]] = Future {
if (someCondition) {
// some code
val amazonClient = Try { /* stuff */ }
amazonClient.map { c =>
// doing some stuff
(new Date, "SomeString")
} else Failure(new Exception)
}
Future.firstCompletedOf(Seq(ftr1, anotherFuture)) map {
case Success((date, myString)) => /* do stuff */
case Failure(e) => log(e)
}

Scala: *.map(*) running in a Future, how to abort map?

#volatile var breakRequested: Boolean = false
// ...
def futureFunc(): Option[Iterable[String]] = {
val result = hugeList.map { item =>
if(breakRequested) {
// put exit code here
// return None
// throw AnException
// what else?
}
item.toText() // time-expensive function
}
Some(result)
}
Future { futureFunc() }
Given somebody set the breakRequested flag to true: How may I exit the map?
What I tried:
return None => such a return transformed into a scala.runtime.NonLocalReturnControl: I tried to catch this error, but it seems to be uncatchable (bypasses try/catch).
object Cancelled extends Exception: I tried to throw this but was not able to catch it as well.
All exceptions show up in the command line, when I run the app through SBT.
If somehow possible, I prefer a solution without try/catch.
For the fast solution, you can convert your hugeList to the Iterator, and then use takeWhile:
...
val result = hugeList.toIterator
.takeWhile(_ => !breakRequested)
.map { item =>
item.text
}
...
Edit:
Scala's Future has no cancellation, but twitter's Future has. To cancel this use method raise.
Also you can write your own map, for example:
#annotation.tailrec def map[T,R](
src: List[T],
cancel: => Boolean,
dsc: List[R] = List.empty[R])(f: T => R):List[R] = src match {
case _ if cancel => dsc
case h :: t => map(t, cancel, f(h) :: dsc)(f)
case Nil => dsc
}
Future{map(hugeList, breakRequested)(_.text)}
If you don't need the result, you can create another future,which will be completed after your breakRequested was changed. And use method Future.firstCompletedOf.

Executing a code block even after catching an exception in Scala

In my current method, I am trying to make a series of calls and if any of them fail, I want to be able to continue running the remainder (while capturing the Exception that was thrown). I am having a hard time figuring this out in Scala.
So in this example, I want to kick off each of these calls - RunA, RunB and RunC but if RunB throws an exception, I want to print that and continue kicking off RunC after that.
var result = Try {
new RunA()
new RunB()
new RunC()
} catch {
case e: Throwable => e.printStackTrace()
false
}
Outside of having them all individually wrapped in a Try/Catch, I am sure there are better ways to do this which is why I am hoping someone can help with this.
I looked at the 'Ignoring' exception but it appears to completely ignore the exception which I want to atleast log.
Thanks!
First, don't mix try { ... } catch { ... } up with scala.util.Try{ ... }.
You can
import scala.util._
val runA = Try{ new RunA }
val runB = Try{ new RunB }
val runC = Try{ new RunC }
and then deal with the exceptions as you see fit. For instance, if you want to print and continue, you could deal with the try statements right there:
def getOrPrint[A](f: => A): Option[A] = Try{ f } match {
case Success(x) => Some(x)
case Failure(e) => e.printStackTrace; None
}
getOrPrint{ new RunA }
...
There can be more elegant ways for such things with scalaz (e.g. read an article here for some inspiration: http://johnkurkowski.com/posts/accumulating-multiple-failures-in-a-ValidationNEL/), but with "only" Scala you can do something like this:
import scala.reflect.ClassTag
import scala.util.{Try, Success, Failure}
def tryAndLog[T: ClassTag] = Try {
implicitly[ClassTag[T]].runtimeClass.newInstance.asInstanceOf[T] // new instance
} match {
case Success(_) => true
case Failure(ex) => ex.printStackTrace ; false
}
def tryRunAll = {
val A = tryAndLog[RunA]
val B = tryAndLog[RunB]
val C = tryAndLog[RunC]
A && B && C // returns true if all invocations succeeded, false otherwise
}
You are mixing scala.util.Try with try {} catch {} which are different concepts. Try wraps function into Success(result) or Failure(error) class, and try-catch is like Java try-catch. I suggest you something like this:
class RunA
class RunB
class RunC
class Result(a: RunA, b: RunB, c: RunC)
implicit class LogFailure[T](t: Try[T]) {
def logFailure: Try[T] = t match {
case scala.util.Failure(err) => err.printStackTrace(); t
case _ => t
}
}
val tryA= Try(new RunA())
val tryB= Try(new RunB())
val tryC = Try(new RunC())
val result: Try[Result] = for {
a <- tryA.logFailure
b <- tryB.logFailure
c <- tryC.logFailure
} yield {
// do smth with a, b, c
new Result(a, b, c)
}
If A, B, C will be successful you'll get Success(Result) if one of them failure you'll get Failure with first exception, however all of them will be logged (printed stack trace)