I have the following list of functions that I need to execute in order.
val steps: List[() => StepResult] = List(step1 _, step2 _, step3 _)
Each step will return a StepResult, which contains the boolean status, and a message:
case class StepResult(success: Boolean, message: String)
The idea is to execute each step in order, but stop going over the list if any of the steps fails. What would be the best way of doing this?
I can go over each step, and execute it:
val results = steps.map { step => step() }
But I'm missing the part of stopping if any of the steps fail. Ideally, I should end up with a List[StepResult] that I can then inspect.
You can use a view to run a map and a takeWhile without iterating through the list twice:
steps.view.map(_()).takeWhile(_.success).force
Views evaluate lazily, and are really handy when you want to call several methods on a collection but only iterate through it once, or only evaluate its contents once. Read more about them here. You can accomplish similar functionality by calling toIterator or toStream instead of view, since those collections operate similarly.
For example:
val step1 = () => { println("running step1"); StepResult(true, "") }
val step2 = () => { println("running step2"); StepResult(true, "") }
val step3 = () => { println("running step3"); StepResult(false, "") }
val step4 = () => { println("running step4"); StepResult(true, "") }
val steps = List(step1, step2, step3, step4)
steps.view.map(s => s()).takeWhile(_.success).force
This will print
running step1
running step2
running step3
Note that running step4 is not printed, since, when using view, the map and takeWhile are used in a single loop. Contrast this with the naive version:
steps.map(s => s()).takeWhile(_.success).toList
Since this doesn't use view, it will run all 4 steps, and print the fourth statement.
If this is a method, you can also use a foldLeft together with a nonlocal return:
def getResults(steps: Seq[() => StepResult]): Seq[StepResult] =
(Seq.empty[StepResult] /: steps) { case (soFar, next) =>
val nextRes = next()
if (nextRes.success) {
soFar :+ nextRes
} else return soFar
}
Or recursively, as explained by Ryan's answer.
You can using recursion:
def doIt(steps: List[() => StepResult]): List[StepResult] = steps match {
case Nil => Nil
case head :: tail =>
val result = head()
if (result.success)
result :: doIt(tail)
else
result :: Nil
}
Related
Say I have a set of rules that have a validation function that returns IO[Boolean] at runtime.
case class Rule1() {
def validate(): IO[Boolean] = IO.pure(false)
}
case class Rule2() {
def validate(): IO[Boolean] = IO.pure(false)
}
case class Rule3() {
def validate(): IO[Boolean] = IO.pure(true)
}
val rules = List(Rule1(), Rule2(), Rule3())
Now I have to iterate through these rules and see "if any of these rules" hold valid and if not then throw exception!
for {
i <- rules.map(_.validate()).sequence
_ <- if (i.contains(true)) IO.unit else IO.raiseError(new RuntimeException("Failed"))
} yield ()
The problem with the code snippet above is that it is trying to evaluate all the rules! What I really want is to exit at the encounter of the first true validation.
Not sure how to achieve this using cats effects in Scala.
I claim that existsM is the most direct way to achieve what you want. It behaves pretty much the same as exists, but for monadic predicates:
for {
t <- rules.existsM(_.validate())
_ <- IO.raiseUnless(t)(new RuntimeException("Failed"))
} yield ()
It also stops the search as soon as it finds the first true.
The raiseUnless is just some syntactic sugar that's equivalent to the if-else from your question.
If you take a look at list of available extension methods in your IDE, you can find findM:
for {
opt <- rules.findM(_.validate())
_ <- opt match {
case Some(_) => IO.unit
case None => IO.raiseError(new RuntimeException("Failed")
}
} yield ()
Doing it manually could be done with foldLeft and flatMap:
rules.foldLeft(IO.pure(false)) { (valueSoFar, nextValue) =>
valueSoFar.flatMap {
case true => IO.pure(true) // can skip evaluating nextValue
case false => nextValue.validate() // need to find the first true IO yet
}
}.flatMap {
case true => IO.unit
case false => IO.raiseError(new RuntimeException("Failed")
}
The former should have the additional advantage that it doesn't have to iterate over whole collection when it finds the first match, while the latter will still go through all items, even if will start discarding them at some point. findM solves that by using tailRecM internally to terminate the iteration on first met condition.
You can try recursive
def firstTrue(rules: List[{def validate(): IO[Boolean]}]): IO[Unit] = rules match {
case r :: rs => for {
b <- r.validate()
res <- if (b) IO.unit else firstTrue(rs)
} yield res
case _ => IO.raiseError(new RuntimeException("Failed"))
}
Another approach is not using booleans at all, but the monad capabilities of IO
def validateRules(rules: List[Rule]): IO[Unit] =
rules.traverse_ { rule =>
rule.validate().flatMap { flag =>
IO.raiseUnless(flag)(new RuntimeException("Failed"))
}
}
I just started working with scala and am trying to get used to the language. I was wondering if the following is possible:
I have a list of Instruction objects that I am looping over with the foreach method. Am I able to add elements to my Instruction list while I am looping over it? Here is a code example to explain what I want:
instructions.zipWithIndex.foreach { case (value, index) =>
value match {
case WhileStmt() => {
---> Here I want to add elements to the instructions list.
}
case IfStmt() => {
...
}
_ => {
...
}
Idiomatic way would be something like this for rather complex iteration and replacement logic:
#tailrec
def idiomaticWay(list: List[Instruction],
acc: List[Instruction] = List.empty): List[Instruction] =
list match {
case WhileStmt() :: tail =>
// add element to head of acc
idiomaticWay(tail, CherryOnTop :: acc)
case IfStmt() :: tail =>
// add nothing here
idiomaticWay(tail, list.head :: acc)
case Nil => acc
}
val updatedList = idiomaticWay(List(WhileStmt(), IfStmt()))
println(updatedList) // List(IfStmt(), CherryOnTop)
This solution works with immutable list, returns immutable list which has different values in it according to your logic.
If you want to ultimately hack around (add, remove, etc) you could use Java ListIterator class that would allow you to do all operations mentioned above:
def hackWay(list: util.List[Instruction]): Unit = {
val iterator = list.listIterator()
while(iterator.hasNext) {
iterator.next() match {
case WhileStmt() =>
iterator.set(CherryOnTop)
case IfStmt() => // do nothing here
}
}
}
import collection.JavaConverters._
val instructions = new util.ArrayList[Instruction](List(WhileStmt(), IfStmt()).asJava)
hackWay(instructions)
println(instructions.asScala) // Buffer(CherryOnTop, IfStmt())
However in the second case you do not need scala :( So my advise would be to stick to immutable data structures in scala.
I have the following code:
// Start async functions
val async1: Future[Seq[Int]] = ...
val async2: Future[Seq[Int]] = ...
val async3: Future[Seq[Int]] = ...
// Wait for completion
(for {
a1 <- async1
a2 <- async2
a3 <- async3
} yield (a1, a2, a3)).map {
// Use the results
}
I want to improve this to handle a variable amount of async functions (and not necessarily calling each of them every time). What I have done so far is:
// Start the async functions ?
val asyncs: Seq[Future[Seq[Int]] = otherList.filter(x => someCondition).map(x => asyncFunc(x))
// Wait for the functions to finish ?
(for (seqInt <- asyncs) yield seqInt).map {
case results => // <-- problem here
// Use the results
}
The problem I am having is that the results are of type Future[Seq[Int]], but I expected they would be of type (Seq[Int], Seq[Int], Seq[Int]) like in the first snippet.
In the end I would like to do is kickoff a dynamic amount of async functions which all have the same Future return type, wait for them all to finish, then use all of their results together.
Future.sequence is the key part I was missing (thanks for the comment)
// Create a list of Futures
val asyncs: Seq[Future[Seq[Int]] = otherList.filter(x => someCondition).map(x => asyncFunc(x))
// Use Future.sequence to to execute them and return a list of sequence of integers
Future.sequence(asyncs).map{
case results => // Use the results List[Seq[Int]]
}.recover {
case error => // Oh no!
}
I am using play2 and reactivemongo to fetch a result from mongodb. Each item of the result needs to be transformed to add some metadata. Afterwards I need to apply some sorting to it.
To deal with the transformation step I use enumerate():
def ideasEnumerator = collection.find(query)
.options(QueryOpts(skipN = page))
.sort(Json.obj(sortField -> -1))
.cursor[Idea]
.enumerate()
Then I create an Iteratee as follows:
val processIdeas: Iteratee[Idea, Unit] =
Iteratee.foreach[Idea] { idea =>
resolveCrossLinks(idea) flatMap { idea =>
addMetaInfo(idea.copy(history = None))
}
}
Finally I feed the Iteratee:
ideasEnumerator(processIdeas)
And now I'm stuck. Every example I saw does some println inside foreach, but seems not to care about a final result.
So when all documents are returned and transformed how do I get a Sequence, a List or some other datatype I can further deal with?
Change the signature of your Iteratee from Iteratee[Idea, Unit] to Iteratee[Idea, Seq[A]] where A is the type. Basically the first param of Iteratee is Input type and second param is Output type. In your case you gave the Output type as Unit.
Take a look at the below code. It may not compile but it gives you the basic usage.
ideasEnumerator.run(
Iteratee.fold(List.empty[MyObject]) { (accumulator, next) =>
accumulator + resolveCrossLinks(next) flatMap { next =>
addMetaInfo(next.copy(history = None))
}
}
) // returns Future[List[MyObject]]
As you can see, Iteratee is a simply a state machine. Just extract that Iteratee part and assign it to a val:
val iteratee = Iteratee.fold(List.empty[MyObject]) { (accumulator, next) =>
accumulator + resolveCrossLinks(next) flatMap { next =>
addMetaInfo(next.copy(history = None))
}
}
and feel free to use it where ever you need to convert from your Idea to List[MyObject]
With the help of your answers I ended up with
val processIdeas: Iteratee[Idea, Future[Vector[Idea]]] =
Iteratee.fold(Future(Vector.empty[Idea])) { (accumulator: Future[Vector[Idea]], next:Idea) =>
resolveCrossLinks(next) flatMap { next =>
addMetaInfo(next.copy(history = None))
} flatMap (ideaWithMeta => accumulator map (acc => acc :+ ideaWithMeta))
}
val ideas = collection.find(query)
.options(QueryOpts(page, perPage))
.sort(Json.obj(sortField -> -1))
.cursor[Idea]
.enumerate(perPage).run(processIdeas)
This later needs a ideas.flatMap(identity) to remove the returning Future of Futures but I'm fine with it and everything looks idiomatic and elegant I think.
The performance gained compared to creating a list and iterate over it afterwards is negligible though.
I am looking at following snippet. When map-getOrElse and nested patten matching increases in the code it doesn't look so elegant. What better options do you suggest?
case MyMessage =>
val image = (request \ "image").asOpt[String]
image.map { im =>
val conf = (request \ "confirmation").asOpt[String]
conf.map { cf =>
//code to retrieve ride
ride match {
case Some(r) =>
if (booleanCondition) sender ! SuccessCommand(JsBoolean(true), command)
else sender ! FailureCommand("Problem updating", command)
case None => sender ! FailureCommand("Ride empty", command)
}
} getOrElse (sender ! FailureCommand("Missing number", command))
} getOrElse (sender ! FailureCommand("Missing image", command))
Whenever you are mapping over an Option with a function that produces an Option, you should consider whether you should be using flatMap:
def f(x: Int): Option[Int] = Some(x + 1)
f(1).flatMap(x => f(x)).flatMap(y => f(y)) // Some(4)
f(1).flatMap(x => f(x)).flatMap(y => f(y)).getOrElse(0) // 4
You can also use for-comprehensions for this, which is really nice for having clean code when you have long chains of these:
(for(x <- f(1); y <- f(x); z <- f(y)) yield z).getOrElse(0)
Another way to tackle this is to return Either[Command,String] from various helper functions, rather than Option. This would then allow you to use a for comprehension, something like the following:
val result = for {
i <- getImage().right
c <- getConf().right
r <- getRide().right
z <- check(r).right
} yield z
// extract either left or right, whichever is occupied
sender ! result.fold(identity, _ => success())
This has the desired property that we stop as soon as we encounter an error, and capture that specific error - or proceed to a successful conclusion.
I think you should be able to collapse a lot of this into Option.fold(), roughly as follows:
case MyMessage =>
sender !
getImage().fold(fail("Missing image")) { im =>
getConf().fold(fail("Missing number")) { conf => // conf isn't used
getRide().fold(fail("Ride empty")) { r =>
if (booleanCondition) succeed(true)
else fail("Problem updating")
}
}
}
This turns out a bit more concise than flatMap and orElse in this situation (see below)
Option.fold(ifEmpty){f} returns ifEmpty (evaluated lazily) if the option was empty, or evaluates the function f if the option was full.
The above code assumes you create helper functions for getting the various Options (or you could inline the relevant code). It also assumes you pull out the creation of commands into a helper function or two, to avoid all the duplicate references to command.
For comparison, a solution using flatMap looks something like:
case MyMessage =>
sender !
getImage().flatMap { im =>
getConf().flatMap { conf =>
getRide().flatMap { r =>
if (booleanCondition) Some(succeed(true))
else Some(fail("Problem updating"))
}.orElse(Some(fail("Ride Empty")))
}.orElse(Some(fail("Missing number")))
}.getOrElse(fail("Missing image"))
which you could shorten very slightly by having variants of your helper methods (fail and succeed) that return Some[Command] rather than Command