Scala's for comprehensions with Future[Option] with no exceptions - scala

I have one Future[Option[String]] function. I need get a transformed string only if future was succesfull and string exists. In other cases I need to get just a None (no any errors or exceptions)
I tried to use for comprehensions (as well as maps/flatMaps) but apparently my result is type Nothing instead of Option[String].
def getOption: Option[String]
def getOptionResult: Future[Option[String]]
def someActions: String
val resultO: Option[String] = for {
myString: String <- getOption
optionResult: Option[String] <- getOptionResult
result: String <- optionResult
} yield {
someActions(result)
}
What's the most elegant way to deal with such situations?

try this
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
def getOptionResult: Future[Option[String]] = Future(Some("Foo"))
// def getOptionResult: Future[Option[String]] = Future(None)
val d = getOptionResult.map(a => a.map(r => "(" + r + ")"))
val res = Await.result(d, 100 nanos).getOrElse(None)

getOptionResult.recover { case _ => None }
if I understood your requirement correctly. This will give an always successful future which has the same result as getOptionResult if that succeeds and None if it fails. See the docs.
Generally you don't want to specify types on the left of <-, it doesn't work at all like specifying them for val/var/def but turns into a pattern match instead.

Related

Convert Seq[Try[Option(String, Any)]] into Try[Option[Map[String, Any]]]

How to conveniently convert Seq[Try[Option[String, Any]]] into Try[Option[Map[String, Any]]].
If any Try before convert throws an exception, the converted Try should throw as well.
Assuming that the input type has a tuple inside the Option then this should give you the result you want:
val in: Seq[Try[Option[(String, Any)]]] = ???
val out: Try[Option[Map[String,Any]]] = Try(Some(in.flatMap(_.get).toMap))
If any of the Trys is Failure then the outer Try will catch the exception raised by the get and return Failure
The Some is there to give the correct return type
The get extracts the Option from the Try (or raises an exception)
Using flatMap rather than map removes the Option wrapper, keeping all Some values and discaring None values, giving Seq[(String, Any)]
The toMap call converts the Seq to a Map
Here is something that's not very clean but may help get you started. It assumes Option[(String,Any)], returns the first Failure if there are any in the input Seq and just drops None elements.
foo.scala
package foo
import scala.util.{Try,Success,Failure}
object foo {
val x0 = Seq[Try[Option[(String, Any)]]]()
val x1 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(None))
val x2 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(Some(("B","two"))))
val x3 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(Some(("B","two"))), Failure(new Exception("bad")))
def f(x: Seq[Try[Option[(String, Any)]]]) =
x.find( _.isFailure ).getOrElse( Success(Some(x.map( _.get ).filterNot( _.isEmpty ).map( _.get ).toMap)) )
}
Example session
bash-3.2$ scalac foo.scala
bash-3.2$ scala -classpath .
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
Type in expressions for evaluation. Or try :help.
scala> import foo.foo._
import foo.foo._
scala> f(x0)
res0: scala.util.Try[Option[Equals]] = Success(Some(Map()))
scala> f(x1)
res1: scala.util.Try[Option[Equals]] = Success(Some(Map(A -> 1)))
scala> f(x2)
res2: scala.util.Try[Option[Equals]] = Success(Some(Map(A -> 1, B -> two)))
scala> f(x3)
res3: scala.util.Try[Option[Equals]] = Failure(java.lang.Exception: bad)
scala> :quit
If you're willing to use a functional support library like Cats then there are two tricks that can help this along:
Many things like List and Try are traversable, which means that (if Cats's implicits are in scope) they have a sequence method that can swap two types, for example converting List[Try[T]] to Try[List[T]] (failing if any of the items in the list are failure).
Almost all of the container types support a map method that can operate on the contents of a container, so if you have a function from A to B then map can convert a Try[A] to a Try[B]. (In Cats language they are functors but the container-like types in the standard library generally have map already.)
Cats doesn't directly support Seq, so this answer is mostly in terms of List instead.
Given that type signature, you can iteratively sequence the item you have to in effect push the list type down one level in the type chain, then map over that container to work on its contents. That can look like:
import cats.implicits._
import scala.util._
def convert(listTryOptionPair: List[Try[Option[(String, Any)]]]): Try[
Option[Map[String, Any]]
] = {
val tryListOptionPair = listTryOptionPair.sequence
tryListOptionPair.map { listOptionPair =>
val optionListPair = listOptionPair.sequence
optionListPair.map { listPair =>
Map.from(listPair)
}
}
}
https://scastie.scala-lang.org/xbQ8ZbkoRSCXGDJX0PgJAQ has a slightly more complete example.
One way to approach this is by using a foldLeft:
// Let's say this is the object you're trying to convert
val seq: Seq[Try[Option[(String, Any)]]] = ???
seq.foldLeft(Try(Option(Map.empty[String, Any]))) {
case (acc, e) =>
for {
accOption <- acc
elemOption <- e
} yield elemOption match {
case Some(value) => accOption.map(_ + value)
case None => accOption
}
}
You start off with en empty Map. You then use a for comprehension to go through the current map and element and finally you add a new tuple in the map if present.
The following solutions is based on this answer to the point that almost makes the question a duplicate.
Method 1: Using recursion
def trySeqToMap1[X,Y](trySeq : Seq[Try[Option[(X, Y)]]]) : Try[Option[Map[X,Y]]] = {
def helper(it : Iterator[Try[Option[(X,Y)]]], m : Map[X,Y] = Map()) : Try[Option[Map[X,Y]]] = {
if(it.hasNext) {
val x = it.next()
if(x.isFailure)
Failure(x.failed.get)
else if(x.get.isDefined)
helper(it, m + (x.get.get._1-> x.get.get._2))
else
helper(it, m)
} else Success(Some(m))
}
helper(trySeq.iterator)
}
Method 2: directly pattern matching in case you are able to get a stream or a List instead:
def trySeqToMap2[X,Y](trySeq : LazyList[Try[Option[(X, Y)]]], m : Map[X,Y]= Map.empty[X,Y]) : Try[Option[Map[X,Y]]] =
trySeq match {
case Success(Some(h)) #:: tail => trySeqToMap2(tail, m + (h._1 -> h._2))
case Success(None) #:: tail => tail => trySeqToMap2(tail, m)
case Failure(f) #:: _ => Failure(f)
case _ => Success(Some(m))
}
note: this answer was previously using different method signatures. It has been updated to conform to the signature given in the question.

Avoiding nested Ifs when working with multiple Options and Eithers

When I am coding with options I find the fold method very useful. Instead of writing if defined statements I can do
opt.fold(<not_defined>){ defined => }
this is good. but what to do if we are working with multiple options. or multiple eithers. Now I have to resort to writing code like
if (x.isDefined && y.isRight) {
val z = getSomething(x.get)
if (z.isDefined) {
....
Depending on the number of things involved, this code becomes very nested.
is there a functional trick to make this code a little un-nested and concise.... like the fold operation above?
have you tried for comprehension? Assuming you don't want to treat individual errors or empty optionals:
import scala.util._
val opt1 = Some("opt1")
val either2: Either[Error, String] = Right("either2")
val try3: Try[String] = Success("try3")
for {
v1 <- opt1
v2 <- either2.right.toOption
v3 <- try3.toOption
} yield {
println(s"$v1 $v2 $v3")
}
Note that Either is not right biased, so you need to call the .right method on the for comprehension (I think cats or scalaz have a right biased Either). Also, we are converting the Either and the Try to optionals, discarding errors
Cases when .isDefined is followed by .get call can be refactored using custom extractors for pattern matching:
def getSomething(s: String): Option[String] = if (s.isEmpty) None else Some(s.toUpperCase)
object MyExtractor {
def unapply(t: (Option[String], Either[Int, String])): Option[String] =
t match {
case (Some(x), Right(y)) => getSomething(x)
case _ => None
}
}
val x: Option[String] = Some("hello world")
val y: Either[Int, String] = Right("ok")
(x, y) match {
case MyExtractor(z) => z // let's do something with z
case _ => "world"
}
// HELLO WORLD
We managed to get rid of all .isDefined, .get and even .right calls by replacing them by explicit pattern matching thanks to our custom extractor MyExtractor.

Conditional chain of futures

I have a sequence of parameters. For each parameter I have to perform DB query, which may or may not return a result. Simply speaking, I need to stop after the first result is non-empty. Of course, I would like to avoid doing unnecessary calls. The caveat is - I need to have this operation(s) contained as a another Future - or any "most reactive" approach.
Speaking of code:
//that what I have
def dbQuery(p:Param): Future[Option[Result]] = {}
//my list of params
val input = Seq(p1,p2,p3)
//that what I need to implements
def getFirstNonEmpty(params:Seq[Param]): Future[Option[Result]]
I know I can possibly just wrap entire function in yet another Future and execute code sequentially (Await? Brrr...), but that not the cleanest solution.
Can I somehow create lazy initialized collection of futures, like
params.map ( p => FutureWhichWontStartUnlessAskedWhichWrapsOtherFuture { dbQuery(p) }).findFirst(!_.isEmpty())
I believe it's possible!
What do you think about something like this?
def getFirstNonEmpty(params: Seq[Param]): Future[Option[Result]] = {
params.foldLeft(Future.successful(Option.empty[Result])) { (accuFtrOpt, param) =>
accuFtrOpt.flatMap {
case None => dbQuery(param)
case result => Future.successful(result)
}
}
}
This might be overkill, but if you are open to using scalaz we can do this using OptionT and foldMap.
With OptionT we sort of combine Future and Option into one structure. We can get the first of two Futures with a non-empty result using OptionT.orElse.
import scalaz._, Scalaz._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
val someF: Future[Option[Int]] = Future.successful(Some(1))
val noneF: Future[Option[Int]] = Future.successful(None)
val first = OptionT(noneF) orElse OptionT(someF)
first.run // Future[Option[Int]] = Success(Some(1))
We could now get the first non-empty Future from a List with reduce from the standard library (this will however run all the Futures) :
List(noneF, noneF, someF).map(OptionT.apply).reduce(_ orElse _).run
But with a List (or other collection) we can't be sure that there is at least one element, so we need to use fold and pass a start value. Scalaz can do this work for us by using a Monoid. The Monoid[OptionT[Future, Int]] we will use will supply the start value and combine the Futures with the orElse used above.
type Param = Int
type Result = Int
type FutureO[x] = OptionT[Future, x]
def query(p: Param): Future[Option[Result]] =
Future.successful{ println(p); if (p > 2) Some(p) else None }
def getFirstNonEmpty(params: List[Param]): Future[Option[Result]] = {
implicit val monoid = PlusEmpty[FutureO].monoid[Result]
params.foldMap(p => OptionT(query(p))).run
}
val result = getFirstNonEmpty(List(1,2,3,4))
// prints 1, 2, 3
result.foreach(println) // Some(3)
This is an old question, but if someone comes looking for an answer, here is my take. I solved it for a use case that required me to loop through a limited number of futures sequentially and stop when the first of them returned a result.
I did not need a library for my use-case, a light-weight combination of recursion and pattern matching was sufficient. Although the question here does not have the same problem as a sequence of futures, looping through a sequence of parameters would be similar.
Here would be the pseudo-code based on recursion.
I have not compiled this, fix the types being matched/returned.
def getFirstNonEmpty(params: Seq[Param]): Future[Option[Result]] = {
if (params.isEmpty) {
Future.successful(None)
} else {
val head = params.head
dbQuery(head) match {
case Some(v) => Future.successful(Some(v))
case None => getFirstNonEmpty(params.tail)
}
}
}

How to accumulate errors in Either?

Suppose I have few case classes and functions to test them:
case class PersonName(...)
case class Address(...)
case class Phone(...)
def testPersonName(pn: PersonName): Either[String, PersonName] = ...
def testAddress(a: Address): Either[String, Address] = ...
def testPhone(p: Phone): Either[String, Phone] = ...
Now I define a new case class Person and a test function, which fails fast.
case class Person(name: PersonName, address: Address, phone: Phone)
def testPerson(person: Person): Either[String, Person] = for {
pn <- testPersonName(person.name).right
a <- testAddress(person.address).right
p <- testPhone(person.phone).right
} yield person;
Now I would like function testPerson to accumulate the errors rather than just fail fast.
I would like testPerson to always execute all those test* functions and return Either[List[String], Person]. How can I do that ?
You want to isolate the test* methods and stop using a comprehension!
Assuming (for whatever reason) that scalaz isn't an option for you... it can be done without having to add the dependency.
Unlike a lot of scalaz examples, this is one where the library doesn't reduce verbosity much more than "regular" scala can:
def testPerson(person: Person): Either[List[String], Person] = {
val name = testPersonName(person.name)
val addr = testAddress(person.address)
val phone = testPhone(person.phone)
val errors = List(name, addr, phone) collect { case Left(err) => err }
if(errors.isEmpty) Right(person) else Left(errors)
}
Scala's for-comprehensions (which desugar to a combination of calls to flatMap and map) are designed to allow you to sequence monadic computations in such a way that you have access to the result of earlier computations in subsequent steps. Consider the following:
def parseInt(s: String) = try Right(s.toInt) catch {
case _: Throwable => Left("Not an integer!")
}
def checkNonzero(i: Int) = if (i == 0) Left("Zero!") else Right(i)
def inverse(s: String): Either[String, Double] = for {
i <- parseInt(s).right
v <- checkNonzero(i).right
} yield 1.0 / v
This won't accumulate errors, and in fact there's no reasonable way that it could. Suppose we call inverse("foo"). Then parseInt will obviously fail, which means there's no way we can have a value for i, which means there's no way we could move on to the checkNonzero(i) step in the sequence.
In your case your computations don't have this kind of dependency, but the abstraction you're using (monadic sequencing) doesn't know that. What you want is an Either-like type that isn't monadic, but that is applicative. See my answer here for some details about the difference.
For example, you could write the following with Scalaz's Validation without changing any of your individual validation methods:
import scalaz._, syntax.apply._, syntax.std.either._
def testPerson(person: Person): Either[List[String], Person] = (
testPersonName(person.name).validation.toValidationNel |#|
testAddress(person.address).validation.toValidationNel |#|
testPhone(person.phone).validation.toValidationNel
)(Person).leftMap(_.list).toEither
Although of course this is more verbose than necessary and is throwing away some information, and using Validation throughout would be a little cleaner.
As #TravisBrown is telling you, for comprehensions don't really mix with error accumulations. In fact, you generally use them when you don't want fine grained error control.
A for comprehension will "short-circuit" itself on the first error found, and this is almost always what you want.
The bad thing you are doing is using String to do flow control of exceptions. You should at all times use Either[Exception, Whatever] and fine tune logging with scala.util.control.NoStackTrace and scala.util.NonFatal.
There are much better alternatives, specifically:
scalaz.EitherT and scalaz.ValidationNel.
Update:(this is incomplete, I don't know exactly what you want). You have better options than matching, such as getOrElse and recover.
def testPerson(person: Person): Person = {
val attempt = Try {
val pn = testPersonName(person.name)
val a = testAddress(person.address)
testPhone(person.phone)
}
attempt match {
case Success(person) => //..
case Failure(exception) => //..
}
}
Starting in Scala 2.13, we can partitionMap a List of Eithers in order to partition elements based on their Either's side.
// def testName(pn: Name): Either[String, Name] = ???
// def testAddress(a: Address): Either[String, Address] = ???
// def testPhone(p: Phone): Either[String, Phone] = ???
List(testName(Name("name")), testAddress(Address("address")), testPhone(Phone("phone")))
.partitionMap(identity) match {
case (Nil, List(name: Name, address: Address, phone: Phone)) =>
Right(Person(name, address, phone))
case (left, _) =>
Left(left)
}
// Either[List[String], Person] = Left(List("wrong name", "wrong phone"))
// or
// Either[List[String], Person] = Right(Person(Name("name"), Address("address"), Phone("phone")))
If the left side is empty, then no elements were Left and thus we can build a Person out of the Right elements.
Otherwise, we return a Left List of the Left values.
Details of the intermediate step (partitionMap):
List(Left("bad name"), Right(Address("addr")), Left("bad phone"))
.partitionMap(identity)
// (List[String], List[Any]) = (List("bad name", "bad phone"), List[Any](Address("addr")))

How can I reverse of flow of Option Monad?

say, I have a bunch of "validation" functions that return None if there is no error, otherwise it return Some(String) specifying the error message. Something like the following ...
def validate1:Option[String]
def validate2:Option[String]
def validate3:Option[String]
I am going to call them in a sequence and as soon as one returns Some(String), I stop and return the same. If it returns None, I go to the next until the sequence is over. If all of them return None, I return None.
I would like to glue them together in a "for expression". Something like ...
for( a <- validate1; b <- validate2; c <- validate3) yield None;
However, Option flows exactly the opposite what I want here. It stops at None and follows with Some(String).
How can I achieve something like that?
You could just chain the calls together with the orElse method on Option
validate1 orElse validate2 orElse validate3
or you could run a fold over a collection of validate methods converted to functions
val vlist= List(validate1 _, validate2 _, validate3 _)
vlist.foldLeft(None: Option[String]) {(a, b) => if (a == None) b() else a}
The scalaz library has a type called Validation which allows for some incredible gymnastics with building both errors and success. For example, suppose you have a few methods which can either return a failure message or some successful outcome (A/B/C):
import scalaz._; import Scalaz._
def fooA : ValidationNEL[String, A]
def fooB : ValidationNEL[String, B]
def fooC : ValidationNEL[String, C]
These can be used with the applicative functor to chain the calls together:
(foo1 <|**|> (foo2, foo3)) match {
case Success( (a, b, c) ) => //woot
case Failure(msgs) => //erk
}
Note that if any one of foo1/2/3 fails, then the whole composition fails with a non-empty list (NEL) of failure messages. If more than one fails, you get all failure messages.
It's a killer app. Examples of how tor return a success and failure are as follows
def foo1 : ValidationNEL[String, Int] = 1.success
def foo2 : ValidationNEL[String, Double] = "some error msg".failNel
Can't you just combine the iterators together and then take the first element? Something like:
scala> def validate1: Option[String] = {println("1"); None}
scala> def validate2: Option[String] = {println("2"); Some("error")}
scala> def validate3: Option[String] = {println("3"); None}
scala> (validate1.iterator ++ validate2.iterator ++ validate3.iterator).next
1
2
res5: String = error
I think you might benefit from using Lift's Box, which has Full (i.e. Some), Empty (i.e. None) and Failure (an Empty with a reason why it's empty and that can be chained). David Pollak has a good blog post introducing it. In short, you might do something like this (not tested):
def validate1: Box[String]
def validate2: Box[String]
def validate3: Box[String]
val validation = for (
validation1 <- validate1 ?~ "error message 1"
validation2 <- validate2 ?~ "error message 2"
validation3 <- validate3 ?~ "error message 3"
) yield "overall success message"
This isn't any shorter than the original example but it's, in my opinion, a bit more logical, with the result of a successful validation in a Full and a failed validation in Failure.
However, we can get smaller. First, since our validation function return Box[String], they can return Failures themselves and we don't need to transform Empty to Failure ourselves:
val validation = for (
validation1 <- validate1
validation2 <- validate2
validation3 <- validate3
) yield "overall success message"
But, Box also has an or method that returns the same Box if it is Full or the other Box if it is not. This would give us:
val validation = validate1 or validate2 or validate3
However, that line stops at the first validation success, not the first failure. It might make sense to make another method that does what you want (perhaps called unless?) though I can't say that it would really be much more useful than the for comprehension approach.
However, here's a little library pimping that does it:
scala> class Unless[T](a: Box[T]) {
| def unless(b: Box[T]) = {
| if (a.isEmpty) { a }
| else b
| }
| }
defined class Unless
scala> implicit def b2U[T](b: Box[T]): Unless[T] = new Unless(b)
b2U: [T](b: net.liftweb.common.Box[T])Unless[T]
scala> val a = Full("yes")
a: net.liftweb.common.Full[java.lang.String] = Full(yes)
scala> val b = Failure("no")
b: net.liftweb.common.Failure = Failure(no,Empty,Empty)
scala> val c = Full("yes2")
c: net.liftweb.common.Full[java.lang.String] = Full(yes2)
scala> a unless b
res1: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless b unless c
res2: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless c unless b
res3: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless c
res4: net.liftweb.common.Box[java.lang.String] = Full(yes2)
This is a quick hack based upon my limited understanding of Scala's type system, as you can see in the following error:
scala> b unless a
<console>:13: error: type mismatch;
found : net.liftweb.common.Full[java.lang.String]
required: net.liftweb.common.Box[T]
b unless a
^
However, that should be enough to get you on the right track.
Of course the Lift ScalaDocs have more information on Box.