I have a list of string ids representing DB records. I'd like to load them from the DB asynchronously, then upload each record to a remote server asynchronously, then when all are done uploading, make a record of the ids of the records that were uploaded.
Since I'm on Scala 2.9.2, I'm using Twitter's core-util Future implementation, but it should work exactly like the 2.10 futures in terms of Monadic transformations.
The general concept is this:
def fetch(id: String): Future[Option[Record]]
def upload(record: Record): Future[String]
def notifyUploaded(ids: Seq[String]): Unit
val ids: Seq[String] = ....
I'm trying to do this via a for comprehension but the fact that fetch returns a Future of Option makes it obscure and the code doesn't compile:
for {
id <- ids
maybeRecord <- fetch(id)
record <- maybeRecord
uploadedId <- upload(record)
} yield uploadedId
Compiling this results in the following error:
scala: type mismatch;
found : com.twitter.util.Future[String]
required: Option[?]
uploadedId <- upload(record)
^
What am I missing? why does the compiler expect uploadedId to be an Option? is there any pretty way I could work around this?
Consider the signature of the flatMap (or bind) function:
trait Monad[M[_]] {
def flatMap[A](a : M[A], f : A => M[B]) : M[B]
....
In your case, you're trying to use flatMap on an Option, giving it an f that generates a Future. But as in the signature above, f should be generating something in the same monad that it's been called on.
Scala isn't necessarily terribly helpful in this regard, since it's pretty good at converting things around (to Seqs, for example) in such a way that you get the impression that you can chain arbitrary flatMap calls together, regardless of the container.
What you possibly want is a 'Monad transformer', which gives you some ability to compose monads. Debasish Ghosh has a post on using Scalaz monad transformers here.
You cannot mix all different types in one for comprehension, I figured out that you might mix Seq and Option and result would be either Seq or Option depending on what is first. It is not possible to mix Future and Seq or Option. If you want to use for-comprehension you would have to cascade them few. In such cases it might be nicer with map/flatMap. I implemented your question in both ways and added types to few intermediate results so that you see the mess that is being created while working with all that different types.
object TestClass {
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._
case class Record(id: String)
def fetch(id: String): Future[Option[Record]] = Future {
Thread.sleep(1000);
Some(Record(id))
}
def upload(record: Record): Future[String] = Future {
Thread.sleep(3000);
record.id + "_uploaded"
}
def notifyUploaded(ids: Seq[String]): Unit = println("notified" + ids)
val ids: Seq[String] = Seq("a", "b", "c")
def main(args: Array[String]): Unit = {
forComprehensionImpl()
mapAndFlatMapImpl()
}
def forComprehensionImpl() = {
val result: Seq[Future[Option[Future[String]]]] = for {
id <- ids
} yield {
for {
maybeRecord <- fetch(id)
} yield {
for {
record <- maybeRecord
} yield {
for {
uploadedId <- upload(record)
} yield uploadedId
}
}
}
val result2: Future[Seq[Option[Future[String]]]] = Future.sequence(result)
val result3: Future[Unit] = result2.flatMap { x: Seq[Option[Future[String]]] =>
Future.sequence(x.flatten).map(notifyUploaded)
}
Await.result(result3, Duration.Inf)
}
def mapAndFlatMapImpl() = {
val res: Seq[Future[Iterable[String]]] = ids.map { id =>
fetch(id).flatMap { maybeRecord =>
val res1: Option[Future[Seq[String]]] = maybeRecord.map { record =>
upload(record) map (Seq(_))
}
res1 match {
case Some(a) => a
case None => Future(Seq())
}
}
}
val res3: Future[Unit] = Future.sequence(res) map (a => notifyUploaded(a.flatten))
Await.result(res3, Duration.Inf)
}
}
Related
I have the following code:
override def getStandsByUser(email: String): Try[Seq[Stand]] =
(for {
user <- OptionT(userService.findOneByEmail(email)): Try[Option[User]]
stands <- OptionT.liftF(standService.list()):[Try[List[Stand]]]
filtered = stands.filter(stand => user.stands.contains(stand.id))
} yield filtered).getOrElse(Seq())
}
I want to add logging on each stage of the processing - so I need to introduce writer monad and stack it with monad transformer OptionT. Could you please suggest how to do that?
The best way to do this is to convert your service calls into using cats-mtl.
For representing Try or Option you can use MonadError and for logging you can use FunctorTell. Now I don't know what exactly you're doing inside your userService or standService, but I wrote some code to demonstrate what the result might look like:
type Log = List[String]
//inside UserService
def findOneByEmail[F[_]](email: String)
(implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[User] = ???
//inside StandService
def list[F[_]]()
(implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[List[Stand]] = ???
def getStandsByUser[F[_]](email: String)
(implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[List[Stand]] =
for {
user <- userService.findOneByEmail(email)
stands <- standService.list()
} yield stands.filter(stand => user.stands.contains(stand.id))
//here we actually run the function
val result =
getStandsByUser[WriterT[OptionT[Try, ?], Log, ?] // yields WriterT[OptionT[Try, ?], Log, List[Stand]]
.run // yields OptionT[Try, (Log, List[Stand])]
.value // yields Try[Option[(Log, List[Stand])]]
This way we can avoid all of the calls to liftF and easily compose our different services even if they will use different monad transformers at runtime.
If you take a look at the definition of cats.data.Writer you will see that it is an alias to cats.data.WriterT with the effect fixed to Id.
What you want to do is use WriterT directly and instead of Id use OptionT[Try, YourType].
Here is a small code example of how that can be achieved:
object Example {
import cats.data._
import cats.implicits._
type MyType[A] = OptionT[Try, A]
def myFunction: MyType[Int] = OptionT(Try(Option(1)))
def main(args: Array[String]): Unit = {
val tmp: WriterT[MyType, List[String], Int] = for {
_ <- WriterT.tell[MyType, List[String]](List("Before first invocation"))
i <- WriterT.liftF[MyType, List[String], Int](myFunction)
_ <- WriterT.tell[MyType, List[String]](List("After second invocation"))
j <- WriterT.liftF[MyType, List[String], Int](myFunction)
_ <- WriterT.tell[MyType, List[String]](List(s"Result is ${i + j}"))
} yield i + j
val result: Try[Option[(List[String], Int)]] = tmp.run.value
println(result)
// Success(Some((List(Before first invocation, After second invocation, Result is 2),2)))
}
}
The type annotations make this a bit ugly, but depending on your use case you might be able to get rid of them. As you can see myFunction returns a result of type OptionT[Try, Int] and WriterT.lift will push that into a writer object that also has a List[String] for your logs.
I'm trying to figure out how to write this piece of code in an elegant pure-functional style using scalaz7 IO and monad transformers but just can't get my head around it.
Just imagine I have this simple API:
def findUuid(request: Request): Option[String] = ???
def findProfile(uuid: String): Future[Option[Profile]] = redisClient.get[Profile](uuid)
Using this API I can easily write impure function with OptionT transformer like this:
val profileT = for {
uuid <- OptionT(Future.successful(findUuid(request)))
profile <- OptionT(findProfile(uuid))
} yield profile
val profile: Future[Option[Profile]] = profileT.run
As you have noticed - this function contains findProfile() with a side-effect. I want to isolate this effect inside of the IO monad and interpret outside of the pure function but don't know how to combine it all together.
def findProfileIO(uuid: String): IO[Future[Option[Profile]]] = IO(findProfile(uuid))
val profileT = for {
uuid <- OptionT(Future.successful(findUuid(request)))
profile <- OptionT(findProfileIO(uuid)) //??? how to put Option inside of the IO[Future[Option]]
} yield profile
val profile = profileT.run //how to run transformer and interpret IO with the unsafePerformIO()???
Any peaces of advice on how it might be done?
IO is meant more for synchronous effects. Task is more what you want!
See this question and answer: What's the difference between Task and IO in Scalaz?
You can convert your Future to Task and then have an API like this:
def findUuid(request: Request): Option[String] = ???
def findProfile(uuid: String): Task[Option[Profile]] = ???
This works because Task can represent both synchronous and asynchronous operations, so findUuid can also be wrapped in Task instead of IO.
Then you can wrap these in OptionT:
val profileT = for {
uuid <- OptionT(Task.now(findUuid(request)))
profile <- OptionT(findProfileIO(uuid))
} yield profile
Then at the end somewhere you can run it:
profileT.run.attemptRun
Check out this link for converting Futures to Tasks and vice versa: Scalaz Task <-> Future
End up with this piece of code, thought it might be useful for someone (Play 2.6).
Controller's method is a pure function since Task evaluation takes place outside of the controller inside of PureAction ActionBuilder. Thanks to Luka's answer!
Still struggling with new paradigm of Action composition in Play 2.6 though, but this is another story.
FrontendController.scala:
def index = PureAction.pure { request =>
val profileOpt = (for {
uuid <- OptionT(Task.now(request.cookies.get("uuid").map(t => uuidKey(t.value))))
profile <- OptionT(redis.get[Profile](uuid).asTask)
} yield profile).run
profileOpt.map { profileOpt =>
Logger.info(profileOpt.map(p => s"User logged in - $p").getOrElse("New user, suggesting login"))
Ok(views.html.index(profileOpt))
}
}
Actions.scala
Convenient action with Task resolution at the end
class PureAction #Inject()(parser: BodyParsers.Default)(implicit ec: ExecutionContext) extends ActionBuilderImpl(parser) {
self =>
def pure(block: Request[AnyContent] => Task[Result]): Action[AnyContent] = composeAction(new Action[AnyContent] {
override def parser: BodyParser[AnyContent] = self.parser
override def executionContext: ExecutionContext = self.ec
override def apply(request: Request[AnyContent]): Future[Result] = {
val taskResult = block(request)
taskResult.asFuture //End of the world lives here
}
})
}
Converters.scala
Task->Future and Future->Task implicit converters
implicit class FuturePimped[+T](root: => Future[T]) {
import scalaz.Scalaz._
def asTask(implicit ec: ExecutionContext): Task[T] = {
Task.async { register =>
root.onComplete {
case Success(v) => register(v.right)
case Failure(ex) => register(ex.left)
}
}
}
}
implicit class TaskPimped[T](root: => Task[T]) {
import scalaz._
val p: Promise[T] = Promise()
def asFuture: Future[T] = {
root.unsafePerformAsync {
case -\/(ex) => p.failure(ex); ()
case \/-(r) => p.success(r); ()
}
p.future
}
}
How can I convert Option[Future[T]] to Future[Option[T]] in scala?
I want to use it in:
val customerAddresses = for {
a <- addressDAO.insert(ca.address) // Future[Address]
ia <- ca.invoiceAddress.map(addressDAO.insert) // Option[Future[Address]]
} yield (a, ia) // Invalid value have to be two futures
Here signature insert method
def insert(address: Address): Future[Address]
ca is a CustomerData
case class CustomerData(address: Address, invoiceAddress: Option[Address])
import scala.concurrent.Future
import scala.concurrent.ExecutionContext
def f[A](x: Option[Future[A]])(implicit ec: ExecutionContext): Future[Option[A]] =
x match {
case Some(f) => f.map(Some(_))
case None => Future.successful(None)
}
Examples:
scala> f[Int](Some(Future.successful(42)))
res3: scala.concurrent.Future[Option[Int]] = Success(Some(42))
scala> f[Int](None)
res4: scala.concurrent.Future[Option[Int]] = scala.concurrent.impl.Promise$KeptPromise#c88a337
If you have cats as a dependency in your application, the most beautiful way would be to use traverse
import cats._
import cats.implicits._
val customerAddresses = for {
a <- addressDAO.insert(ca.address) // Future[Address]
ia <- ca.invoiceAddress.traverse(addressDAO.insert) // Future[Option[Address]]
} yield (a, ia)
The standard library does provide the methods to use Future.sequence on an Option, unfortunately you have to plumb them together.
Either as a quick method:
def swap[M](x: Option[Future[M]]): Future[Option[M]] =
Future.sequence(Option.option2Iterable(x)).map(_.headOption)
Note I found the implicit Option.option2Iterable was already in scope for me. So you may not need to provide it, reducing the code down to Future.sequence(x).map(_.headOption)
Or you may prefer an extension method:
implicit class OptionSwitch[A](f: Option[Future[A]]) {
import scala.concurrent.Future
def switch: Future[Option[A]] = Future.sequence(Option.option2Iterable(f))
.map(_.headOption)
}
val myOpt = Option(Future(3))
myOpt.switch
Here is another solution:
def swap[T](o: Option[Future[T]]): Future[Option[T]] =
o.map(_.map(Some(_))).getOrElse(Future.successful(None))
The trick is to convert Option[Future[T]] into Option[Future[Option[T]]] which is easy, and then extract the value from that Option.
When you have a list (or any TraversableOnce) of futures and want a single future for computing the whole list, you use Future.sequence or Future.traverse. You can think of an Option like a list of 1 or 0 elements but since is technically not a list you have to go for a little conversion in this case. Anyway, this is a code that does it normally:
val optionFuture:Option[Future[String]] = ???
val futureOption:Future[Option[String]] = Future.sequence(optionFuture.toIterable).map(_.headOption)
In you example use better Future.traverse:
val customerAddresses = for {
a <- addressDAO.insert(ca.address) // Future[Address]
ia <- Future.traverse(ca.invoiceAddress.toIterable)(addressDAO.insert).map(_.headOption) // Future[Option[Address]]
} yield CustomerData(a, ia) // Types OK
val customerAddresses = for {
a <- addressDAO.insert(ca.address) // Future[Address]
ia <- ca.invoiceAddress.map(x => addressDAO.insert(x).map(_.map(k => Some(k))).getOrElse(Future.successful(None)))
} yield (a, ia)
You can do it simply using Dumonad
import io.github.dumonad.dumonad.Implicits._
futureOfOption.dummed
In the following snippet,
trait MyType1; trait MyType2
import scala.concurrent.Promise
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
I pass in p1 and p2 to another function where I complete the Promise using a successful Future. After the call to this function, I try to read the value in the Promise:
trait Test {
// get the Future from the promise
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem <- f1
f1Elem <- someF1Elem
f2Elem <- f1Elem
} yield {
// do something with f1Elem and f2Elem
"..."
}
}
When I try to compile this, I get some compiler issues.
Error:(52, 19) type mismatch;
found : Option[Nothing]
required: scala.concurrent.Future[?]
flElem <- someF1Elem
^
IntelliJ shows no errors or what-so-ever and the types look aligned. But I'm not sure why the compiler is unhappy! Any clues?
Your for comprehension types must be consistent, so you cannot freely mix Option and Future in the way you do.
In your example, f1 returns a Future[Option[MyType1]] while f2 returns a Future[MyType2]
Remember that a for comprehension desugars to a series of flatMap/map and potentially withFilter.
Also the (simplified) signatures of flatMap for Future[A] and Option[A] are
def flatMap[B](f: A => Future[B]): Future[B]
def flatMap[B](f: A => Option[B]): Option[B]
The first two steps of the for-comprehension desugar to
f1.flatMap { someF1Elem =>
someF1Elem.flatMap { f1Elem => // this returns a `Option[MyType1]`
...
}
}
see the error now?
Now, to fix this there's a few approaches you can follow. One very handy is to use Monad Transformers, which allow you to combine (for instance) Future and Option into a single monad type OptionT.
Specifically you can go back and forth from Future[Option[A]] to OptionT[Future, A]. The basic idea is that you can flatMap on the latter and extract a value of type A.
But before that, you need to make your types of the "right shape", so that both are a Future[Option[Something]]
Here's an example using scalaz
import scalaz._; import Scalaz._ ; import scalaz.OptionT._
import scala.concurrent.{ Promise, Future }
import scala.concurrent.ExecutionContext.Implicits.global
trait MyType1
trait MyType2
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
val f1 = p1.future
val f2 = p2.future
val res = for {
f1Elem <- optionT(f1)
f2Elem <- f2.liftM[OptionT]
} yield {
println(s"$f1Elem $f2Elem")
}
optionT builds an OptionT[Future, A] given a Future[Option[A]], while liftM[OptionT] achieves the same when you have a Future[A]
The type of res is OptionT[Future, Unit] in this case, and you can get a Future[Option[Unit]] by calling run on it.
Looking at "How does yield work", your for comprehension is equivalent to
f1.flatMap(someF1Elem: Option[MyType1] =>
someF1Elem.flatMap(f1Elem =>
f1Elem.map(f2Elem =>
"..."
)
)
)
Basically what happens is this: flatMap on a Future is defined to take a function that returns another Future.
This gives you a similar error:
<console>:64: error: value map is not a member of MyType1
f1Elem.map(f2Elem =>
^
<console>:63: error: type mismatch;
found : Option[Nothing]
required: scala.concurrent.Future[?]
someF1Elem.flatMap(f1Elem =>
^
If you want the operation to perform asynchronously, i.e. really mapping the Future instances, you will have to get back to a Future[Option[X]]. As Kim suggested, you can nest the comprehension:
import scala.concurrent.{Future, Promise}
def test: Future[Option[String]] = {
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
// ... run code that completes the promises
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem: Option[MyType1] <- f1
f2Elem <- f2
} yield {
for (f1Elem <- someF1Elem) yield "something"
}
}
You could also make the resulting Future fail when the option is not defined (NoSuchElementException)
def test: Future[String] = {
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
// ... run code that completes the promises
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem: Option[MyType1] <- f1
f2Elem <- f2
} yield {
val f1Elem = someF1Elem.get // may cause an exception and fail the future
"something"
}
}
In a for comprehension, the expressions on the right hand side of the <- have to have compatible types. That is because for comprehensions are basically syntactic sugar for nested flatMap calls. To solve this problem, you can either have a for comprehension nested within another for comprehension or you can use methods like get or map on the Options.
Another option is to use monad transformers, but that's beyond the scope of this answer.
I have a collection of methods that return different types:
Either[ErrorResponse, X]
Future[Either[ErrorResponse, X]]
Option[ErrorResponse]
These methods need the result from a previous method to perform their computation. The methods:
type Parameters = Map[String, String]
// allows me to flatmap on an either
implicit def toRightProjection[Failure, Success](e: Either[Failure, Success]) =
e.right
// converts anything to a future
implicit def toFuture[T](t: T) =
Future.successful(t)
// retrieves the request paramters from the given request
def requestParameters(request: RequestHeader): Either[ErrorResponse, Parameters] = ???
// retrieves the response type from the given parameters
def responseType(p: Parameters): Either[ErrorResponse, String] = ???
// retrieves the client id from the given parameters
def clientId(p: Parameters): Either[ErrorResponse, String] = ???
// retrieves the client using the given client id
def client(clientId: String): Future[Either[ErrorResponse, Client]] = ???
// validates the response type of the client
def validateResponseType(client: Client, responseType: String): Option[ErrorResponse] = ???
I can the wire them together with the following for comprehension (note that I wrote down some types to clarify the contents of specific parts of the computation).
val result: Either[ErrorResponse, Future[Either[ErrorResponse, Client]]] =
for {
parameters <- requestParameters(request)
clientId <- clientId(parameters)
responseType <- responseType(parameters)
} yield {
val result: Future[Either[ErrorResponse, Either[ErrorResponse, Client]]] =
for {
errorOrClient <- client(clientId)
client <- errorOrClient
} yield validateResponseType(client, responseType).toLeft(client)
result.map(_.joinRight)
}
val wantedResult: Future[Either[ErrorResponse, Client]] =
result.left.map(Future successful Left(_)).merge
The above code is quite messy and I feel this can be done differently. I read about monads and monad transformers. The concept of those is very new to me and I can not get my head around it.
Most of the examples only deal with two types of results: Either[X, Y] and Future[Either[X, Y]]. I still find it very hard to bend my mind around it.
How can I write a nice and clean for comprehension that replaces the above one?
Something like this would be awesome (I am not sure if that is even possible):
val result: Future[Either[ErrorResponse, Client]] =
for {
parameters <- requestParameters(request)
clientId <- clientId(parameters)
responseType <- responseType(parameters)
client <- client(clientId)
_ <- validateResponseType(client, responseType)
}
OK, here is my attempt at this:
import scalaz._, Scalaz._
implicit val futureMonad = new Monad[Future] {
override def point[A](a: ⇒ A): Future[A] = future(a)
override def bind[A, B](fa: Future[A])(f: A ⇒ Future[B]): Future[B] =
fa.flatMap(f)
}
import EitherT._
val result: EitherT[Future, ErrorResponse, Client] =
for {
parameters <- fromEither(Future(requestParameters(request)))
clientId <- fromEither(Future(clientId(parameters)))
responseType <- fromEither(Future(responseType(parameters)))
client <- fromEither(client(clientId))
response <- fromEither[Future, ErrorResponse, Client](Future(validateResponseType(client, responseType).toLeft(client)))
} yield response
val x: Future[\/[ErrorResponse, Client]] = result.run
scala.util.Either is not a Monad, but the scalaz library has a great implementation.
object Test extends ToIdOps {
import scalaz.{ Monad, Functor, EitherT, \/, -\/, \/- }
import scalaz.syntax.ToIdOps
implicit val FutureFunctor = new Functor[Future] {
def map[A, B](a: Future[A])(f: A => B): Future[B] = a map f
}
implicit val FutureMonad = new Monad[Future] {
def point[A](a: => A): Future[A] = Future(a)
def bind[A, B](fa: Future[A])(f: (A) => Future[B]): Future[B] = fa flatMap f
}
def someMethod: Future[\/[InvalidData, ValidData]] = {
// things went well
ValidData.right // this comes from ToIdOps
// or something went wrong
InvalidData.left
}
def someOtherMethod: Future[\/[InvalidData, ValidData]] // same as above
val seq = for {
d <- EitherT(someMethod)
y <- EitherT(someOtherMethod)
} yield { // whatever}
// you can now Await.result(seq.run, duration)
// you can map or match etc with \/- and -\/
val result = seq.run map {
case -\/(left) => // invalid data
case \/-(right) => // game on
}
}
There is no really clean way to do comprehensions over multiple monad types. In ScalaZ there is OptionT that might help, worth checking out. You could also transform your Eithers to Options or the other way around and be able to have a little bit less of a mess. A third option might be to create your own kind of wrapper that combines Future[Either|Option] into the same monad and then comprehend over that.
For reference I asked aboutish the same question on the play framework mailing list recently and got some good links in the replies: https://groups.google.com/d/topic/play-framework/JmCsXNDvAns/discussion