How to use "ask" for three value in akka - scala

I have an actor who sends a message to three actors, and waits for the response of all three actors in order to proceed.
The actors return a datatype of the form: List[(String, Double, String)].
I want them all sorted according to the Double value of the Tuple3.
So far the code i've written is:
implicit val timeout = Timeout(5.seconds)
val x = actorOne ? "hey"
val y = actorTwo ? "you"
val z = actorThree ? "there"
val answer = for{
a <- x.mapTo[List[(String, Double, String)]]
b <- y.mapTo[List[(String, Double, String)]]
c <- z.mapTo[List[(String, Double, String)]]
} yield a ++ a ++ a sortBy(_._2)
How do i make sure the actor doesn't proceed until all three actors have responded?
Thanks

Your for-comprehension will only get evaluated after a, b, and c are evaluated, so you do not have to do anything there. If you mean that you have some later come which relies on the value of answer, then you can put it inside onComplete:
answer.onComplete {
case Success(x) => // do something on success
case Failure(ex) => // report failure
}
You can use Promise to interact with the results in the right time. E.g. if your outer method is supposed to return Future[Boolean], you can do like this:
def myFunction():Future[Boolean] = {
val p = Promise[Boolean]
implicit val timeout = Timeout(5.seconds)
val x = actorOne ? "hey"
val y = actorTwo ? "you"
val z = actorThree ? "there"
val answer = for{
a <- x.mapTo[List[(String, Double, String)]]
b <- y.mapTo[List[(String, Double, String)]]
c <- z.mapTo[List[(String, Double, String)]]
} yield a ++ a ++ a sortBy(_._2)
answer.onComplete {
case Success(x) =>
// do something with x
p.success(true)
case Failure(ex) =>
// process faliure
p.success(false)
}
p.future
}

This code prints the result list:
List((b1,2.03,b1), (c0,3.5,c0), (c0,3.5,c0), (b0,4.03,b0), (a0,4.1,a0), (a1,4.31,a1))
to stdout (you use 'a' in the yield part three times in your code!).
What did you exactly mean with 'actor doesn't proceed until all three actors have responded'?
the actor should wait for the result - done in the example code
the actor should 'stash' all incoming messages till the result is available?
import scala.concurrent.{Future, Await}
import scala.concurrent.duration._
import akka.util.Timeout
import scala.concurrent.ExecutionContext.Implicits.global
implicit val timeout = Timeout(5.seconds)
val x = Future(List(("a0", 4.1, "a0"), ("a1", 4.31, "a1")))
val y = Future(List(("b0", 4.03, "b0"), ("b1", 2.03, "b1")))
val z = Future(List(("c0", 3.5, "c0"), ("c0", 3.5, "c0")))
val answer = for{
a <- x.mapTo[List[(String, Double, String)]]
b <- y.mapTo[List[(String, Double, String)]]
c <- z.mapTo[List[(String, Double, String)]]
} yield a ++ b ++ c sortBy(_._2)
// don't use the result -> execute a side effect
answer.foreach { res =>
println(res)
}
// !!ONLY FOR TESTING!!
Await.result(answer, 1.minute)
Thread.sleep(1000)

Related

Function signature for async processing with errors accumulation

Suppose I've got a function fab: A => Future[B]. Now I need to write new function foo to process Seq[A] and accumulate all errors. That's why I cannot use Future.traverse because it "fails fast" and doesn't accumulate the errors.
foo receives Seq[A] and should return a Future. The client should get either B or an exception for each element of the input Seq[A]. What would be a signature of this function ?
To define foo for what you need, consider using Future.sequence on top of map/recover after applying fab to individual elements of the input list, as shown below:
import scala.concurrent.{ Future, ExecutionContext }
def foo[A, B](ls: List[A])(fab: A => Future[B])(implicit ec: ExecutionContext):
Future[List[Either[Throwable, B]]] =
Future.sequence(ls.map(fab).map(_.map(Right(_)).recover{ case e => Left(e) }))
Note that instead of Seq, immutable List is preferred hence is being used here. Change it to Seq if necessary.
Testing foo:
implicit val ec = ExecutionContext.global
def fab(s: String): Future[Int] = Future{ 10 / s.length }
val ls = List("abcd", "", "xx", "")
foo(ls)(fab)
// res1: Future[List[Either[Throwable, Int]]] = Future(Success(List(
// Right(2),
// Left(java.lang.ArithmeticException: / by zero),
// Right(5),
// Left(java.lang.ArithmeticException: / by zero)
// )))
I have a solution with ZIO.
I added this fake function:
def fab(implicit ec: ExecutionContext): Int => Future[String] = i => Future(
if (i % 3 == 0)
throw new IllegalArgumentException(s"bad $i")
else
s"$i"
)
Now we create a Stream of Int's and run fab for each of them
val stream =
ZStream.fromIterable(Seq(1, 2, 3, 4, 5))
.map(in => Task.fromFuture(implicit ec => fab(ec)(in)))
val sink = Sink.collectAll[Task[String]]
Now we collect the successes and failures:
val collect: ZIO[zio.ZEnv, Throwable, (List[String], List[Throwable])] = for {
strs <- stream.run(sink)
successes <- Task.collectAllSuccesses(strs)
failures <- ZIO.collectAllSuccesses(strs.map(_.flip))
} yield (successes, failures)
Running and printing this:
new DefaultRuntime {}
.unsafeRun(
collect
.tapError { ex => zio.console.putStrLn(s"There was an exception: ${ex.getMessage}") }
.tap { case (successes, failures) => zio.console.putStrLn(s"($successes, $failures)") }
.fold(_ => -1, _ => 0)
)
Prints us:
(List(1, 2, 4, 5), List(java.lang.IllegalArgumentException: bad 3))
Let me know if you are need more explaining - if ZIO is an option.

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

How to flatten a sequence of cats' ValidatedNel values

I need to flatten a sequence of cats.data.ValidatedNel[E, T] values to a single ValidatedNel value:
val results: Seq[cats.data.ValidatedNel[E, T]] = ???
val flattenedResult: cats.data.ValidatedNel[E, T]
I can do it like this:
import cats.std.list._, cats.syntax.cartesian._
results.reduce(_ |#| _ map { case _ => validatedValue })
but wonder if a pre-defined library methods exists.
It depends on how you want to combine them (what is validatedValue in your question ?)
import cats.data.{Validated, ValidatedNel}
import cats.implicits._
val validations1 = List(1.validNel[String], 2.valid, 3.valid)
val validations2 = List(1.validNel[String], "kaboom".invalidNel, "boom".invalidNel)
If you want to combine the Ts, you can use Foldable.combineAll which uses a Monoid[T] :
val valSum1 = validations1.combineAll
// Valid(6)
val valSum2 = validations2.combineAll
// Invalid(OneAnd(kaboom,List(boom)))
If you want to get a ValidationNel[String, List[T]], you can use Traverse.sequence :
val valList1: ValidatedNel[String, List[Int]] = validations1.sequence
// Valid(List(1, 2, 3))
val valList2: ValidatedNel[String, List[Int]] = validations2.sequence
// Invalid(OneAnd(kaboom,List(boom)))
If you don't care about the result, which seems to be the case, you can use Foldable.sequence_.
val result1: ValidatedNel[String, Unit] = validations1.sequence_
// Valid(())
val result2: ValidatedNel[String, Unit] = validations2.sequence_
// Invalid(OneAnd(kaboom,List(boom)))
validations1.sequence_.as(validatedValue) // as(x) is equal to map(_ => x)

Converting multiple optional values in Scala

I am writing a function that receives several optional String values and converts each one to either an Int or a Boolean and then passes the converted values to Unit functions for further processing. If any conversion fails, the entire function should fail with an error. If all conversions succeed, the function should process the converted values and return a success.
Here is the function I have written (simplified from the actual):
f(x: Option[String], y: Option[String], z: Option[String]): Result = {
val convertX = x.map(value => Try(value.toInt))
val convertY = y.map(value => Try(value.toBoolean))
val convertZ = z.map(value => Try(value.toBoolean))
val failuresExist =
List(convertX, convertY, convertZ).flatten.exists(_.isFailure)
if (failuresExist) BadRequest("Cannot convert input")
else {
convertX.foreach {
case Success(value) => processX(value)
case _ =>
}
convertY.foreach {
case Success(value) => processY(value)
case _ =>
}
convertZ.foreach {
case Success(value) => processZ(value)
case _ =>
}
Ok()
}
}
Although this solution will probably work, it is very awkward. How can I improve it?
A more imperative style could work, if you don't mind that.
def f(x: Option[String], y: Option[String], z: Option[String]): Result = {
try {
val convertX = x.map(_.toInt)
val convertY = y.map(_.toBoolean)
val convertZ = z.map(_.toBoolean)
convertX.foreach(processX)
convertY.foreach(processY)
convertZ.foreach(processZ)
Ok()
} catch {
case _: IllegalArgumentException | _: NumberFormatException => BadRequest("Cannot convert input")
}
}
If you're using scalaz I would use the Option applicative and ApplicativeBuilder's |#| combinator. If any of the inputs are None, then the result is also None.
import scalaz.std.option.optionInstance
import scalaz.syntax.apply._
val result: Option[String] =
Some(1) |#| Some("a") |#| Some(true) apply {
(int, str, bool) =>
s"int is $int, str is $str, bool is $bool"
}
In pure scala, you could use flatMap on option:
val result: Option[String] =
for {
a <- aOpt
b <- bOpt
c <- cOpt
} yield s"$a $b $c"
I personally prefer the applicative because it makes it clear that the results are independent. for-blocks read to me like "first do this with a, then this with b, then this with c" whereas applicative style is more like "with all of a, b, and c, do ..."
Another option with scalaz is sequence, which inverts a structure like T[A[X]] into A[T[X]] for traversable T and applicative A.
import scalaz.std.option.optionInstance
import scalaz.std.list.listInstance
import scalaz.syntax.traverse._
val list: List[Option[Int]] = List(Option(1), Option(4), Option(5))
val result: Option[List[Int]] = list.sequence
// Some(List(1, 4, 5))
For completence I am adding the a piece of code here that process the values are required. However if this is better than that the original is debatable. If you want to process all the value and gather the results of the transformation scalaz Validator could be a better option.
import scala.util.Try
val x = Some("12")
val y = Some("false")
val z = Some("hello")
def process(v: Boolean) = println(s"got a $v")
def processx(v: Int) = println(s"got a number $v")
// Abstract the conversion to the appropriate mapping
def mapper[A, B](v: Option[String])(mapping: String => A)(func: Try[A] => B) = for {
cx <- v.map(vv => Try(mapping(vv)))
} yield func(cx)
def f(x: Option[String], y: Option[String], z: Option[String]) = {
//partially apply the function here. We will use that method twice.
def cx[B] = mapper[Int, B](x)(_.toInt) _
def cy[B] = mapper[Boolean, B](y)(_.toBoolean) _
def cz[B] = mapper[Boolean, B](z)(_.toBoolean) _
//if one of the values is a failure then return the BadRequest,
// else process each value and return ok
(for {
vx <- cx(_.isFailure)
vy <- cy(_.isFailure)
vz <- cz(_.isFailure)
if vx || vy || vz
} yield {
"BadRequest Cannot convert input"
}) getOrElse {
cx(_.map(processx))
cy(_.map(process))
cz(_.map(process))
"OK"
}
}
f(x,y,z)
In the case a "short circuit" behaviour is required the following code will work.
import scala.util.Try
val x = Some("12")
val y = Some("false")
val z = Some("hello")
def process(v: Boolean) = println(s"got a $v")
def processx(v: Int) = println(s"got a number $v")
def f(x: Option[String], y: Option[String], z: Option[String]) =
(for {
cx <- x.map(v => Try(v.toInt))
cy <- y.map(v => Try(v.toBoolean))
cz <- z.map(v => Try(v.toBoolean))
} yield {
val lst = List(cx, cy, cz)
lst.exists(_.isFailure) match {
case true => "BadRequest Cannot convert input"
case _ =>
cx.map(processx)
cy.map(process)
cz.map(process)
"OK"
}
}) getOrElse "Bad Request: missing values"
f(x,y,z)