scala's Try elegant on error behavior - scala

Is there a more elegant way of doing this in scala?
def doTheDangerousThing(): Try[Result] = {
val result = Try(dangerousOp)
if (result.isFailure) {
println("error")
}
result
}

I think your if statement is perfectly valid. Here is another alternative:
def doTheDangerousThing(): Try[Result] = Try(dangerousOp) recoverWith {
case exception => println("error"); Failure(exception)
}

Something like this:
def doTheDangerousThing[Result](dangerousOp: =>Result): Try[Result] = Try(dangerousOp) match {
case o # Failure(_) => println("error"); o
case _ => _
}

Not sure if this is more idiomatic, but sometimes I find that placing the recoverWith in this manner improves readability for me:
def doDangerousThing(): Try[Result] = Try {
dangerousOp
} recoverWith {
case t: Throwable => println("error"); Failure(t)
}

My preferred,
def doTheDangerousThing(): Option[Result] = Try (dangerousOp) toOption
If the Try is successful you will get a Some(value), if it fails a None.
For a large compilation on Try uses, have a look at Try introduced in Scala 2.10.0 .

There are ways. For instance:
def doTheDangerousThing(): Try[Result] = {
val result = Try(dangerousOp)
result.failed foreach { _ =>
println("error")
}
result
}
Or, if you don't want to repeat result all through, then:
def doTheDangerousThing(): Try[Result] = {
Try(dangerousOp) recover {
case ex => println("error"); throw ex
}
}

Well, I suppose you could do something like this:
def doTheDangerousThing(): Option[Result] =
Try(dangerousOp) match {
case Success(result) => Some(result)
case Failure(e) => None //might want to log the error as well
}

In some cases I love to use two-step approach which will allow me a more granular error message control:
def retrieveData(dataId: String): Try[String] = {
Try {
Option(someApi(dataId))
.getOrElse(throw SomeApiFailedException("invalid dataId"))
} recoverWith {
case e: SomeApiFailedException => Failure(e)
case e: Throwable => Failure(SomeApiFailedException("failed retrieve dataId"))
}
}
case class SomeApiFailedException(err: String) extends RuntimeException(err)

I could choose from either of the three implementations, depending on whether I want to:
Simply propagate it upwards ( doTheDangerousThing1 )
Ignore the error ( doTheDangerousThing2 )
Intercept the error while propagating it upwards ( doTheDangerousThing3 )
Here is the code:
import scala.util.{Try,Success,Failure}
object temp {
type Result = Int
def dangerousOp = {
val r = scala.util.Random.nextInt(10)
if (r > 5) r else throw new RuntimeException("Failed on " + r)
}
def logMessage[T](t: T) = println(t)
def doTheDangerousThing1(): Try[Result] = Try(dangerousOp)
def doTheDangerousThing2(): Option[Result] = {
Try(dangerousOp) match {
case Success(r) => Option(r)
case _ => None
}
}
def doTheDangerousThing3(): Try[Result] = {
Try(dangerousOp) match {
case t # Success(r) => t
case t # _ => logMessage("failed: "+t); t
}
}
}
Inside the REPL
scala> doTheDangerousThing1
res0: scala.util.Try[Result] = Success(9)
scala> doTheDangerousThing1
res1: scala.util.Try[Result] = Success(9)
scala> doTheDangerousThing2
res2: Option[Result] = None
scala> doTheDangerousThing2
res3: Option[Result] = Some(7)
scala> doTheDangerousThing3
failed: Failure(java.lang.RuntimeException: Failed on 0)
res4: scala.util.Try[Result] = Failure(java.lang.RuntimeException: Failed on 0)
scala> doTheDangerousThing3
failed: Failure(java.lang.RuntimeException: Failed on 0)
res5: scala.util.Try[Result] = Failure(java.lang.RuntimeException: Failed on 0)

Related

How should I get B form A => B

I'm new to Scala, and I'm running into this strange situation.
def bar[A, B](implicit foo: A => B): B = {
// do something
foo
}
And then I got error like
require B but found A => B
How should I get B form A => B
Here's the reason why I did this, I have two functions:
def funcA: String = {
def getStrA: String = "A"
// then there's the same operation in both functions
Try{ } match {
case Success(_) => getStrA
case Failure(_) => // exactlly same error handler in both function
}
}
def funcB: Int = {
def doSomething(x: Int): Int = {
// do something
x / 1
}
val x = 1
Try{ } match {
case Success(_) => doSomething(1)
case Failure(_) => // exactlly same error handler in both function
}
}
Here's what I want to achieve
def funcA: String = {
implicit def getStrA: String = "A"
bar
}
def funcB: Int = {
val x = 1
implicit def doSomething(x: Int): Int = {
// do something
x / 1
}
bar
}
def bar[A, B](implicit foo: A => B): B = {
Try{ } match {
case Success(_) => foo
case Failure(_) => // exactlly same error handler in both function
}
}
You have a conversion from A to B. You need to return B. The only way to do this is to pass A into the function. This signature has an implied assumption that you have some valid A value (most likely hardcoded) that you will always use here.
def bar[A, B](implicit foo: A => B): B = {
val a: A = ... // hmm...
foo(a)
}
Considering, that A is parametric, then you are either missing some information, or this A is impossible to create (it cannot be null because not all types can take null as a value), so you might need to throw exception in such case. Probably you are either missing some A provider or you should always fail this operation.
UPDATE:
There is no need for using implicits at all in your code:
def bar[B](f: onSuccess: A => B) =
Try{ some operations } match {
case Success(value) => onSuccess(value)
case Failure(_) => // error handler
}
def funcA = bar(_ => "A")
def funcB = bar(_ => 1)

How to get Left from a method that returns Future?

def myMethod(myType: String) :Future[Future[Either[List[MyError], MyClass]]] {
for {
first <- runWithSeq(firstSource)
}
yield {
runWithSeq(secondSource)
.map {s ->
val mine = MyClass(s.head, lars)
val errors = myType match {
case "all" => Something.someMethod(mine)
}
(s, errors)
}
.map { x =>
x._2.leftMap(xs => {
addInfo(x._1.head, xs.toList)
}).toEither
}
}
}
for {
myStuff <- myMethod("something")
} yield {
myStuff.collect {
case(Left(errors), rowNumber) =>
MyCaseClass(errors, None) //compilation error here
}
}
I get compilation error on MyCaseClass that expected: List[MyError], found: Any
The signature of MyCaseClass is:
case class MyCaseClass(myErrors: List[ValidationError])
How can I fix this such that I can correctly call MyCaseClass inside the yield?
Your code example doesn't make much sense, and doesn't compile, but if runWithSeq() returns a Future then you should be able to eliminate the double Future return type like so.
for {
_ <- runWithSeq(firstSource)
scnd <- runWithSeq(secondSource)
} yield { ...
Your example is pretty hard to paste and fix
Abstact example for this
Class C may be whatever you want
def test(testval: Int)(implicit ec: ExecutionContext): Future[Future[Either[String, Int]]] = {
Future(Future{
if (testval % 2 == 0) Right(testval) else Left("Smth wrong")
})
}
implicit class FutureEitherExt[A, B](ft: Future[Either[A, B]]) {
def EitherMatch[C](f1: A => C, f2: B => C)(implicit ec: ExecutionContext): Future[C] = {
ft.map {
case Left(value) => f1(value)
case Right(value) => f2(value)
}
}
}
val fl: Future[Either[String, Int]] = test(5).flatten
val result: Future[String] = fl.EitherMatch(identity, _.toString)

Type conversion from Unit to Future[Boolean]

I have the following function and I would like to return Future[Boolean] but the IDE prompts that I return Unit. I am new in Scala. Can someone point me out what I am doing wrong?
def remove(loginInfo: LoginInfo): Future[Boolean] = {
val result = findObject(loginInfo)
result.onSuccess {
case Some(persistentPasswordInfo) =>
val removeResult = remove(persistentPasswordInfo._id.toString)
removeResult.map {
case Left(ex) => Future.successful(false)
case Right(b) => Future.successful(b)
}
case None => Future.successful(false)
}
}
Replace onSuccess with flatMap. Assuming your remove(x: String) method also returns a Future, that will also need to be flatMapped:
def remove(loginInfo: LoginInfo): Future[Boolean] = {
val result = findObject(loginInfo)
result.flatMap {
case Some(persistentPasswordInfo) =>
val removeResult = remove(persistentPasswordInfo._id.toString)
removeResult.flatMap {
case Left(ex) => Future.successful(false)
case Right(b) => Future.successful(b)
}
case None => Future.successful(false)
}
}

Returning multiple collections from flatMap

I am working on a method that has 3 possible outcomes for multiple items: Error, Invalid and Success. For each of these I need to return a json list identifying which items were in error, invalid and successful.
My current attempt follows. I have used Object to represent the class my objects are as fully explaining would take too long. The Object class has a method process which returns a boolean to indicate success or error and throws an exception when the object is invalid:
def process(list: List[Objects]) = {
val successIds = new ListBuffer[Int]();
val errorIds = new ListBuffer[Int]();
val invalidIds = new ListBuffer[Int]();
list.foreach( item => {
try {
if (item.process) {
successIds ++ item.id
} else {
errorIds ++ item.id
}
} catch {
case e: Exception => invalidIds ++ item.id
}
})
JsonResult(
Map("success" -> successIds,
"failed" -> errorIds,
"invalid" -> invalidIds)
)
}
Problem is using Mutable data structures isn't very "Scala-y". I would prefer to build up these lists in some more functional way but I am quite new to scala. Any thoughts or hints as to how this might be done?
My though is using something like the flatMap method that takes a tuple of collections and collates them in the same way the flatMap method does for a single collection:
def process(list: List[Objects]) = {
val (success, error, invalid) = list.flatMap( item => {
try {
if (item.process) {
(List(item.id), List.empty, List.empty)
} else {
(List.empty, List(item.id), List.empty)
}
} catch {
case e: Exception =>
(List.empty, List.empty, List(item.id))
}
})
JsonResult(
Map("success" -> success,
"failed" -> error,
"invalid" -> invalid)
)
}
flatMap isn't what you need here - you need groupBy:
def process(list: List[Objects]) = {
def result(x: Objects) =
try if (x.process) "success" else "failed"
catch {case _ => "invalid"}
JsonResult(list groupBy result mapValues (_ map (_.id)))
}
There's always recursion:
class Ob(val id: Int) { def okay: Boolean = id < 5 }
#annotation.tailrec def process(
xs: List[Ob],
succ: List[Int] = Nil,
fail: List[Int] = Nil,
invalid: List[Int] = Nil
): (List[Int], List[Int], List[Int]) = xs match {
case Nil => (succ.reverse, fail.reverse, invalid.reverse)
case x :: more =>
val maybeOkay = try { Some(x.okay) } catch { case e: Exception => None }
if (!maybeOkay.isDefined) process(more, succ, fail, x.id :: invalid)
else if (maybeOkay.get) process(more, x.id :: succ, fail, invalid)
else process(more, succ, x.id :: fail, invalid)
}
Which works as one would hope (skip the reverses if you don't care about order):
scala> process(List(new Ob(1), new Ob(7), new Ob(2),
new Ob(4) { override def okay = throw new Exception("Broken") }))
res2: (List[Int], List[Int], List[Int]) = (List(1,2),List(7),List(4))
Adapted to make it compile without "Objects"
def procex (item: String): Boolean = ((9 / item.toInt) < 1)
def process (list: List[String]) = {
val li: List[(Option[String], Option[String], Option[String])] = list.map (item => {
try {
if (procex (item)) {
(Some (item), None, None)
} else {
(None, Some (item), None)
}
} catch {
case e: Exception =>
(None, None, Some (item))
}
})
li
}
// below 10 => failure
val in = (5 to 15).map (""+_).toList
// 0 to throw a little exception
val ps = process ("0" :: in)
val succeeders = ps.filter (p=> p._1 != None).map (p=>p._1)
val errors = ps.filter (p=> p._2 != None).map (p=>p._2)
val invalides = ps.filter (p=> p._3 != None).map (p=>p._3)
What doesn't work:
(1 to 3).map (i=> ps.filter (p=> p._i != None).map (p=>p._i))
_i doesn't work.

Scala: match and parse an integer string?

I'm looking for a way to matching a string that may contain an integer value. If so, parse it. I'd like to write code similar to the following:
def getValue(s: String): Int = s match {
case "inf" => Integer.MAX_VALUE
case Int(x) => x
case _ => throw ...
}
The goal is that if the string equals "inf", return Integer.MAX_VALUE. If the string is a parsable integer, return the integer value. Otherwise throw.
Define an extractor
object Int {
def unapply(s : String) : Option[Int] = try {
Some(s.toInt)
} catch {
case _ : java.lang.NumberFormatException => None
}
}
Your example method
def getValue(s: String): Int = s match {
case "inf" => Integer.MAX_VALUE
case Int(x) => x
case _ => error("not a number")
}
And using it
scala> getValue("4")
res5: Int = 4
scala> getValue("inf")
res6: Int = 2147483647
scala> getValue("helloworld")
java.lang.RuntimeException: not a number
at scala.Predef$.error(Predef.scala:76)
at .getValue(<console>:8)
at .<init>(<console>:7)
at .<clinit>(<console>)
at RequestResult$.<init>(<console>:4)
at RequestResult$.<clinit>(<console>)
at RequestResult$result(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Na...
This is better IMHO:
val IntRegEx = "(\\d+)".r
def getValue(s: String): Option[Int] =
s match {
case "inf" => Some(Int.MaxValue)
case IntRegEx(num) => Some(num.toInt)
case _ => None
}
getValue("inf") // Some(2147483647)
getValue("123412") // Some(123412)
getValue("not-a-number") // None
Of course, it doesn't throw any exceptions, but if you really want it, you may use:
getValue(someStr).getOrElse(error("NaN"))
You could use a guard:
def getValue(s: String): Int = s match {
case "inf" => Integer.MAX_VALUE
case _ if s.matches("[+-]?\\d+") => Integer.parseInt(s)
}
How about:
def readIntOpt(x: String) =
if (x == "inf")
Some(Integer.MAX_VALUE)
else
scala.util.Try(x.toInt).toOption
an improved version of James Iry's extractor:
object Int {
def unapply(s: String) = scala.util.Try(s.toInt).toOption
}
Since Scala 2.13 introduced String::toIntOption:
"5".toIntOption // Option[Int] = Some(5)
"abc".toIntOption // Option[Int] = None
we can cast the String as an Option[Int] after checking if it's equal to "inf":
if (str == "inf") Some(Int.MaxValue) else str.toIntOption
// "inf" => Option[Int] = Some(2147483647)
// "347" => Option[Int] = Some(347)
// "ac4" => Option[Int] = None
def getValue(s: String): Int = s match {
case "inf" => Integer.MAX_VALUE
case _ => s.toInt
}
println(getValue("3"))
println(getValue("inf"))
try {
println(getValue("x"))
}
catch {
case e => println("got exception", e)
// throws a java.lang.NumberFormatException which seems appropriate
}