I am trying to chain together some basic functions using Futures returned from a slick action and I'm hitting some pretty trivial stumbling blocks.
Both the andThen and onSuccess methods require a PartialFunction passed as a parameter. My understanding is probably quite flawed but after reading about anonymous functions it seems like andThen needs to know your anonymous function with cater for any Success or Failure input.
Given onSuccess already only caters for the Success case why does it still need to be a PartialFunction?
This block of code my indicate the problem I am having:
val db = Database.forConfig("h2mem1")
try {
val f = db.run(setupCommands)
.onSuccess { println(_) }
Await.ready(f, 10.seconds )
}
finally db.close
I get a compile error:
[error] found : Unit => Unit
[error] required: PartialFunction[Unit,?]
[error] .onSuccess { println(_) }
They did it so you can pattern match on the result, though I agree that it seems needless, I don't really use onSuccess and prefer to map and flatMap my futures:
val f = Future.successful("test")
f.onSuccess({
case "test" => println("Worked")
case x: String => println(s"Kind of worked: $x")
})
In the case of more advanced data types I could see this being more useful:
val fOpt = Future.successful(Option("Test"))
fOpt.onSuccess({
case Some(x) => println(x)
case None => println("None")
})
Really this is probably just coming from the actor api since when you ask an actor you don't know the return type, you need to pattern match on it since it's Any:
val actor:ActorRef = ???
val fAny = actor ? "asking"
fAny.onSuccess({
case x:String => println(s"Something String $x")
case x:Int => println(s"Something Int $x")
case x => println(s"Something else $x")
})
Well, you can just pass it a PartialFunction, if it needs one:
db.run(setupCommands).onSuccess(PartialFunction(println))
Or:
db.run(setupCommands).onSuccess { case result => println(result) }
Related
I have many functions in my code defined with return type as Either[Throwable, String] and all of them have one argument of type String. Three representative functions of my code are defined as:
val f1 = (input: String) => {
/* Processing from the input using a library in my actual code returns a Either[Throwable, String] */
if (input == "a") Left(new Exception(input))
else Right("Success")
}
val f2 = (input: String) => {
if (input == "b") Left(new Exception(input))
else Right("Success")
}
val f3 = (input: String) => {
if (input == "c") Left(new Exception(input))
else Right("Success")
}
To chain the function outputs, I'm writing code like:
def x(input: String) = f1(input) match {
case Left(value) => Left(value)
case Right(_) => f2(input) match {
case Left(value) => Left(value)
case Right(_) => f3(input)
}
}
Since this is just three functions so this might look like a short code. However there are multiple such matches that are happening in my code, so it's a very long code. I am looking to avoid such a chaining.
I know that Scala has a way to chain functions like f1.andThen(f2).andThen(f3), however the problem is that in each andThen we need to pass the same argument, in this case being input. However I want to chain these functions so that if there is a Left output, it should not go to the next andThen.
I believe this can be simplified using Functional Programming, but I don't know where to start. Is there a way we can achieve this using Scala functional programming?
If you have cats in scope, then all you need to do is this:
import cats.syntax.all._
val functions: List[String => Either[Throwable, Unit]] = List(
// put your functions here.
)
val result: Either[Throwable, Unit] =
functions.traverse_(f => f(input))
Otherwise, you may emulate it using this:
val init: Either[Throwable, Unit] = Right(())
functions.foldLeft(init) {
case (acc, f) =>
acc.flatMap(_ => f(input))
}
I found out that in order to pattern match Future fur Success/Failure, I need to use andThen (or onComplete, onSuccess...) and cannot use map. Why is that?
What I wanted to do (simplified, I am matching for Success and so on as well):
val f1 = Future(throw new Exception("Oops"))
f1 map { case Failure(e) => ??? }
Gives:
error: constructor cannot be instantiated to expected type;
found : scala.util.Failure[T]
required: Nothing
f1 map { case Failure(e) => ??? }
What I ended up doing:
val f1 = Future(throw new Exception("Oops"))
f1 andThen { case Failure(e) => ??? }
I would like to understand why map cannot be used here.
The answer is in the signature of map: it takes a A => B and returns a Future[B]. If you will, you can look at a Future as follows:
type Future[A] = Async[Either[Throwable, A]]
Future#map, Future#flatMap and Future.apply view this "stack" of types as a single big thing with a hole (Future is basically a special cased monad transformer). When you map/flatMap on a Future, you are only operating on the inner A.
Because the type signature isn't correct. When you want to map over a Future[A], you need to provide a function taking an A and producing a B, which isn't what you seem to be doing. What you're looking for is recover:
f1 recover { case e => // e is already a `Throwable` here ??? }
I am trying to find a cleaner way to express code that looks similar to this:
def method1: Try[Option[String]] = ???
def method2: Try[Option[String]] = ???
def method3: Try[Option[String]] = ???
method1 match
{
case f: Failure[Option[String]] => f
case Success(None) =>
method2 match
{
case f:Failure[Option[String]] => f
case Success(None) =>
{
method3
}
case s: Success[Option[String]] => s
}
case s: Success[Option[String]] => s
}
As you can see, this tries each method in sequence and if one fails then execution stops and the base match resolves to that failure. If method1 or method2 succeeds but contains None then the next method in the sequence is tried. If execution gets to method3 its results are always returned regardless of Success or Failure. This works fine in code but I find it difficult to follow whats happening.
I would love to use a for comprehension
for
{
attempt1 <- method1
attempt2 <- method2
attempt3 <- method3
}
yield
{
List(attempt1, attempt2, attempt3).find(_.isDefined)
}
because its beautiful and what its doing is quite clear. However, if all methods succeed then they are all executed every time, regardless of whether an earlier method returns a usable answer. Unfortunately I can't have that.
Any suggestions would be appreciated.
scalaz can be of help here. You'll need scalaz-contrib which adds a monad instance for Try, then you can use OptionT which has nice combinators. Here is an example:
import scalaz.OptionT
import scalaz.contrib.std.utilTry._
import scala.util.Try
def method1: OptionT[Try, String] = OptionT(Try(Some("method1")))
def method2: OptionT[Try, String] = OptionT(Try(Some("method2")))
def method3: OptionT[Try, String] = { println("method 3 is never called") ; OptionT(Try(Some("method3"))) }
def method4: OptionT[Try, String] = OptionT(Try(None))
def method5: OptionT[Try, String] = OptionT(Try(throw new Exception("fail")))
println((method1 orElse method2 orElse method3).run) // Success(Some(method1))
println((method4 orElse method2 orElse method3).run) // Success(Some(method2))
println((method5 orElse method2 orElse method3).run) // Failure(java.lang.Exception: fail)
If you don't mind creating a function for each method, you can do the following:
(Try(None: Option[String]) /: Seq(method1 _, method2 _, method3 _)){ (l,r) =>
l match { case Success(None) => r(); case _ => l }
}
This is not at all idiomatic, but I would like to point out that there's a reasonably short imperative version also with a couple tiny methods:
def okay(tos: Try[Option[String]]) = tos.isFailure || tos.success.isDefined
val ans = {
var m = method1
if (okay(m)) m
else if ({m = method2; okay(m)}) m
method3
}
The foo method should do the same stuff as your code, I don't think it is possible to do it using the for comprehension
type tryOpt = Try[Option[String]]
def foo(m1: tryOpt, m2: tryOpt, m3: tryOpt) = m1 flatMap {
case x: Some[String] => Try(x)
case None => m2 flatMap {
case y: Some[String] => Try(y)
case None => m3
}
}
method1.flatMap(_.map(Success _).getOrElse(method2)).flatMap(_.map(Success _).getOrElse(method3))
How this works:
The first flatMap takes a Try[Option[String]], if it is a Failure it returns the Failure, if it is a Success it returns _.map(Success _).getOrElse(method2) on the option. If the option is Some then it returns the a Success of the Some, if it is None it returns the result of method2, which could be Success[None], Success[Some[String]] or Failure.
The second map works similarly with the result it gets, which could be from method1 or method2.
Since getOrElse takes a by-name paramater method2 and method3 are only called if they need to be.
You could also use fold instead of map and getOrElse, although in my opinion that is less clear.
From this blog:
def riskyCodeInvoked(input: String): Int = ???
def anotherRiskyMethod(firstOutput: Int): String = ???
def yetAnotherRiskyMethod(secondOutput: String): Try[String] = ???
val result: Try[String] = Try(riskyCodeInvoked("Exception Expected in certain cases"))
.map(anotherRiskyMethod(_))
.flatMap(yetAnotherRiskyMethod(_))
result match {
case Success(res) => info("Operation Was successful")
case Failure(ex: ArithmeticException) => error("ArithmeticException occurred", ex)
case Failure(ex) => error("Some Exception occurred", ex)
}
BTW, IMO, Option is no need here?
I'd like to implement validation for a sequence of operations that all return Either[Error,Item]
It should be fail-fast (in my initial need), I mean, returning Either[Error,Seq[Item]].
If there is an error, it's obvious i do not want the following operations to be performed.
But in the future i may want to collect all the errors instead of returning only the first one.
I know Scalaz can do the job but for now I quite don't understand all parts of Scalaz and I'm pretty sure there's a simpler way to do it without using Scalaz, but using by-name parameters for exemple.
Is there a way to store by-name parameters in a sequence?
So that i can create a sequence of by-name values that represent my operations?
I mean, some kind of type Seq[=> Either[Error,Item]]
Then I could do something like calling takeWhile or collectFirst or something somilar, without all the operations being performed before the creation of the sequence?
I would expect the operations to be performed only when iterating on the sequence.
Thanks
You can indeed use a Seq[() => Either[Error, Item]] to defer the computation at collection creation time. So for example
val doSomething1: () => Either[Error, Item] = () => { println(1); Right(1) }
val doSomething2: () => Either[Error, Item] = () => { println(2); Right(2) }
val doSomething3: () => Either[Error, Item] = () => { println(3); Left("error") }
val doSomething4: () => Either[Error, Item] = () => { println(4); Right(3) }
val doSomething5: () => Either[Error, Item] = () => { println(5); Left("second error") }
val l = Seq(doSomething1, doSomething2, doSomething3, doSomething4, doSomething5)
(Items are Ints in the example and Errors are Strings)
Then you can process them lazily stopping at first failure using the following recursive function:
def processUntilFailure(l: Seq[() => Either[Error, Item]]): Either[Error, Seq[Item]] = {
l.headOption.map(_.apply() match {
case Left(error) => Left(error)
case Right(item) => processUntilFailure(l.tail).right.map(_ :+ item)
}).getOrElse(Right(Nil))
}
So now when I run processUntilFailure(l)
scala> processUntilFailure(l)
1
2
3
res1: Either[Error,Seq[Item]] = Left(error)
If you wanted to generate a Either[Seq[String], Seq[Int]] (processing all the operations). You could do it with a little change:
def processAll(l: Seq[() => Either[Error, Item]]): Either[Seq[Error], Seq[Item]] = {
l.headOption.map(_.apply() match {
case Left(error) => processAll(l.tail) match {
case Right(_) => Left(Seq(error))
case Left(previousErrors) => Left(previousErrors :+ error)
}
case Right(item) => processAll(l.tail).right.map(_ :+ item)
}).getOrElse(Right(Nil))
}
The only change as you can see is the Left case in the pattern match. Running this one:
scala> processAll(l)
1
2
3
4
5
res0: Either[Seq[Error],Seq[Item]] = Left(List(second error, error))
processAll can be replaced with a generic foldLeft on l
val zero: Either[Seq[Error], Seq[Item]] = Right(Seq[Item]())
l.foldLeft(zero) { (errorsOrItems: Either[Seq[Error], Seq[Item]], computation: () => Either[String, Int]) =>
computation.apply().fold(
{ (error: String) => Left(errorsOrItems.left.toOption.map(_ :+ error).getOrElse(Seq(error))) },
{ (int: Int) => errorsOrItems.right.map(_ :+ int) })
}
processUntilFailure can as well but not easily. Since aborting early from a fold is tricky. Here's a good answer about other possible approaches when you find yourself needing to do that.
You should be able to pull this off with the type Seq[Function0[Either[Error, Item]]]. Function0 is, obviously, a zero-argument function. The rest should be self-explanatory.
Scalaz provides the type IO for exactly this purpose, so you could actually use that as well. You may not want to yet, however, if you're just beginning to work with Scalaz.
is it possible to match Option[Map[String,String]] for some key at once (e.g. without nested matches)?
The following snippet is how it is now:
val myOption:Option[Map[String,String]] = ...
myOption match {
case Some(params) =>
params get(key) match {
case Some(value) => Ok(value)
case None => BadRequest
case None => BadRequest
}
Sure! Just flatMap that sh*t!
def lookup(o: Option[Map[String, String]], k: String) =
o.flatMap(_ get k).map(Ok(_)).getOrElse(BadRequest)
If you're using Scala 2.10 you can fold over the Option:
def lookup(o: Option[Map[String, String]], k: String) =
o.flatMap(_ get k).fold(BadRequest)(Ok(_))
(for (params <- myOption; value <- params.get(key)) yield Ok(value)).getOrElse(BadRequest)
You should be able to do this using a couple of higher-order functions. I think this does what you want:
myOption.collect {
case m if (m contains key) => Ok(m(key))
} getOrElse BadRequest
collect takes a partial function, and the getOrElse handles the case where the partial function returned None, which translates it to your BadRequest case.