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)
Related
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.
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
}
I'm getting this error when i run the below code -
type mismatch, found : scala.collection.immutable.IndexedSeq[Int] required: Range
Where I'm going wrong ?
Functions -
def calcRange(i: Int, r: List[Range]):List[Range] = r match {
case List() => List(new Range(i,i+1,1))
case r1::rs =>
if (r1.start-1==i) {new Range(i,r1.end,1):: rs; }
else if(r1.end==i){new Range(r1.start,r1.end+1,1)::rs}
else {r1::calcRange(i,rs)}
}
def recurseForRanges(l: Iterator[Int]):List[Range] = {
var ans=List[Range]()
while(l.hasNext){
val cur=l.next;
ans=calcRange(cur,ans)
}
ans
}
def rangify(l: Iterator[Int]):Iterator[Range] = recurseForRanges(l).toIterator
Driver code
def main(args: Array[String]) {
val x=rangify( List(1,2,3,6,7,8).toIterator ).reduce( (x,y) => x ++ y)
/** This line gives the error -type mismatch,
found : scala.collection.immutable.IndexedSeq[Int] required: Range */
}
You can check docs:
++[B](that: GenTraversableOnce[B]): IndexedSeq[B]
++ returns IndexedSeq, not another Range, Range cannot have "holes" in them.
One way to fix it is to change Ranges to IndexedSeqs before reducing. This upcasts the Range so that reduce could take function
(IndexedSeq[Int], IndexedSeq[Int]) => IndexedSeq[Int]
because now it takes
(Range, Range) => Range
But ++ actually returns IndexedSeq[Int] instead of Range hence the type error.
val x = rangify(List(1, 2, 3, 6, 7, 8).iterator).map(_.toIndexedSeq).reduce(_ ++ _)
You can as well do this kind of cast by annotating type:
val it: Iterator[IndexedSeq[Int]] = rangify(List(1,2,3,6,7,8).iterator)
val x = it.reduce(_ ++ _)
Note that your code can be simplified, without vars
def calcRange(r: List[Range], i: Int): List[Range] = r match {
case Nil =>
Range(i, i + 1) :: Nil
case r1 :: rs =>
if (r1.start - 1 == i)
Range(i, r1.end) :: rs
else if (r1.end == i)
Range(r1.start, r1.end + 1) :: rs
else
r1 :: calcRange(rs, i)
}
def recurseForRanges(l: Iterator[Int]): List[Range] = {
l.foldLeft(List.empty[Range])(calcRange)
}
def rangify(l: Iterator[Int]): Iterator[Range] = recurseForRanges(l).iterator
val x = rangify(List(1,2,3,6,7,8).iterator).map(_.toIndexedSeq).reduce(_ ++ _)
To explain what I've done with it:
Range has a factory method, you don't need new keyword, you don't need to specify by value because 1 is default.
You need no semicolons as well.
What you are doing in recurseForRanges is basically what foldLeft does, I just swapped arguments in calcRange it could be passed directly to foldLeft.
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)
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)