I have the following code:
Some(db.run(unionPermissionQuery.result).map(_.map(_.name).toList))
and I get the following error:
[error] found : scala.concurrent.Future[List[String]]
[error] required: List[String]
[error] Some(db.run(unionPermissionQuery.result).map(_.map(_.name).toList)),
[error] ^
[error] one error found
So I suppose I have to convert the Future[List[String]] to List[String]. I am new to scala sorry if that's too easy.
Full code:
def find(loginInfo: LoginInfo): Future[Option[models.admin.User]] = {
val userQuery = for {
dbLoginInfo <- loginInfoQuery(loginInfo)
dbUserLoginInfo <- Userlogininfo.filter(_.logininfoid === dbLoginInfo.id)
dbUser <- User.filter(_.userid === dbUserLoginInfo.userid)
} yield dbUser
db.run(userQuery.result.headOption).map { dbUserOption =>
dbUserOption.map { user =>
val permissionQuery = for {
dbUserPermission <- Userpermission.filter(_.userid === user.userid)
dbPermission <- Permission.filter(_.id === dbUserPermission.permissionid)
} yield dbPermission
val rolePermissionQuery = for {
dbUserRole <- Userrole.filter(_.userid === user.userid)
dbRole <- Role.filter(_.id === dbUserRole.roleid)
dbRolePermission <- Rolepermission.filter(_.roleid === dbRole.id)
dbPermission <- Permission.filter(_.id === dbRolePermission.permissionid)
} yield dbPermission
val unionPermissionQuery = permissionQuery union rolePermissionQuery
models.admin.User(
UUID.fromString(user.userid),
user.firstname,
user.lastname,
user.jobtitle,
loginInfo,
user.email,
user.emailconfirmed,
Some(db.run(unionPermissionQuery.result).map(_.map(_.name).toList)),
user.enabled)
}
}
I just want to get the user and then fill all permissions. Individual permissions and permissions inherited by the role assigned to the user.
Is it better to get the user and then perform another request to get the permissions based on the user id? I don't think so.
General information on futures
You will find all information on Futures you need on
http://docs.scala-lang.org/overviews/core/futures.html.
A possible approach is awaiting the result, but it is not good to use in a prod application.
val myFutureResult : Future[T] = Future {...}
val myResult : T = Await.result(myFutureResult, secondsToWait seconds)
Normally instead of awaiting the result and storing it in a variable you can map the future and compose it and only use Await at the last moment.
val myFutureResult : Future[T] = Future {...}
def myFunctionOnT(in: T) = ...
for {res <- myFutureResult } yield myFunctionOnT(res)
Play and Futures
Play itself can handle Future[T]by default by using Action.async {} instead of Action{} here you can find more: https://www.playframework.com/documentation/2.5.x/ScalaAsync
This information applied to extended question
db.run(unionPermissionQuery.result).map(_.map(_.name).toList).map { permission =>
models.admin.User(
UUID.fromString(user.userid),
user.firstname,
user.lastname,
user.jobtitle,
loginInfo,
user.email,
user.emailconfirmed,
Some(permission),
user.enabled)
}
I'm answering the question in the title.
The only way to implement the function Future[A] => A in a sensible way (and without access to a time-machine) is to await the completion of the future, as a value Future[A] indicates a value of type A will yield in the future.
Check your library for the correct method to wait. For the Scala std lib it is .result. As commented by Jean, the correct way for the Scala std lib is to use Await.result().
Note that waiting is a blocking method call. If you want to avoid this (and there are some good reasons to do so), you should check out Andreas' answer.
Related
I am using the below code to fetch news from a remote site in my Play Scala application server (2.5.x). I am using the suggested mechanism from "https://www.playframework.com/documentation/2.5.x/ScalaWS".
The use case is in 2 steps:
Call remote API-2 to fetch the news content using a cached token.
If the token is invalid/expired, call remote API-1 to get a token and cache it.
Problem: I am not able to avoid step #2 even if the cached token is valid. Therefore I am fetching the token for each call of this Action in listRemoteNews.
If I use if-else conditions, then I get compile errors as below. Note that I have tried for comprehensions, too, but I need to still use if conditions there. Then I get the same compile errors.
[error] /sandbox/git/play-scala-app.repo/app/controllers/remoteapi/ListRemoteNewsController.scala:26: overloaded method value async with alternatives:
[error] [A](bodyParser: play.api.mvc.BodyParser[A])(block: play.api.mvc.Request[A] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[A] <and>
[error] (block: play.api.mvc.Request[play.api.mvc.AnyContent] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error] (block: => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent]
[error] cannot be applied to (scala.concurrent.Future[Any])
[error] def listRemoteNews = Action.async {
Code section below (requestOne and requestTwo are methods forming WSRequest and returning Future[WSResponse]):
#Singleton
class ListRemoteNewsController #Inject()(ws: WSClient)(implicit context: ExecutionContext) extends Controller {
def listRemoteNews = Action.async {
println("<--- Stored Token 1: " + remoteToken)
val responseTwo: Future[WSResponse] = requestTwo.withQueryString("token" -> remoteToken).get()
val resp: Future[JsValue] = responseTwo.map {
response =>
response.json
}
resp.map {
response => {
Ok(response)
}
}
println("<-- Get token...")
val responseOne: Future[WSResponse] = requestOne
val resp2: Future[String] = responseOne.map {
response => {
remoteToken = (response.json \ "token_list" \ "token").as[String]
println("<--- Obtained token 2: " + remoteToken)
remoteToken
}
}
val responseThree: Future[WSResponse] = requestTwo.withQueryString("token" -> remoteToken).get()
responseThree.map {
response => {
println("<--- status: " + response.status)
Ok(response.json)
}
}
}
Futures are things that must be composed. Right now, you're making a number of different requests, which each return futures, but you're not returning those futures or combining them together in any way. Mapping a future is pointless if you don't do anything with the new future returned by the map function.
What you need to do is compose your futures, and this is done using methods like flatMap.
So here's roughly what I'm guessing your code should look like:
requestTwo.withQueryString("token" -> remoteToken).get().flatMap { response =>
if (tokenWasValid(response)) {
Future.successful(Ok(response.json))
} else {
requestOne.get().flatMap { tokenResponse =>
val remoteToken = (response.json \ "token_list" \ "token").as[String]
requestTwo.withQueryString("token" -> remoteToken).get()
}.map { response =>
Ok(response.json)
}
}
}
If the above makes no sense whatsoever, then I recommend you stop and read up on Scala futures before doing anything else.
Here's a selection of tutorials I found by doing a quick Google search:
http://code.hootsuite.com/introduction-to-futures-in-scala/
https://danielasfregola.com/2015/04/01/how-to-compose-scala-futures/
https://doc.akka.io/docs/akka/current/scala/futures.html
I have an action which processes form submissions. Before validating the form, I need to resolve two Futures. I thought I could just nest everything, meaning put the fold inside the yield block of the for comprehension.
For example:
def handleFormSubmission = silhouette.SecuredAction.async { implicit request =>
for {
user <- userService.findOneByUserId(userId)
avatar <- avatarService.findOneByUserId(userId)
} yield {
myForm.bindFromRequest.fold(
formWithErrors => formWithErrorsBranch(formWithErrors, user, avatar),
changeData => changeDataBranch(changeData, user, avatar))
}
}
Both branches return Future[Result] and the signature of fold is def fold[R](hasErrors: Form[T] => R, success: T => R): R. To my understanding, fold takes two functions with an argument Form[T] and T and both return R. That would mean that if I return in both branches Future[Result], fold will also return Future[Result]. However, since it is wrapped inside a for comprehension to resolve both Futures user and avatar, I would not need to have Future[Result] but instead Result. Is that correct? If so, how can I fix the following compile error in a non-blocking manner?
type mismatch;
found : scala.concurrent.Future[play.api.mvc.Result]
required: play.api.mvc.Result
If I understand your problem correctly, this is how it can be solved:
def handleFormSubmission = silhouette.SecuredAction.async { implicit request =>
for {
user <- userService.findOneByUserId(userId)
avatar <- avatarService.findOneByUserId(userId)
response <- myForm.bindFromRequest.fold(
formWithErrors => formWithErrorsBranch(formWithErrors, user, avatar),
changeData => changeDataBranch(changeData, user, avatar)
)
} yield response
}
simply, don't yield so soon. If you are writing for comprehension and something is returning future, extract the value with <- and then yield it.
<- is translated into flatMap with signature (simplified) flatMap(f: A => Future[B]): Future[B]. So if your function returns a future and you don't want to get nested Future[Future[A]], use flatMap. yield is desugared to map with signature map(f: A => B): Future[B] so this is for the case when f does not return a Future unless you want it nested for some reason. If both of your branches for folding on form binding result return a Future[A] then you want to use flatMap.
On a side note, you are not obtaining user and avatar concurrently, but one after another. You might want to start the computations first and then "wait" for both to complete. This can be done in several ways, for example:
val userFuture = userService.findOneByUserId(userId)
val avatarFuture = avatarService.findOneByUserId(userId)
for {
user <- userFuture
avatar <- avatarFuture
response <- ...
} yield response
or for example with zip
for {
(user, avatar) <- userService.findOneByUserId(userId) zip avatarService.findOneByUserId(userId)
response <- ...
} yield response
Zip and flatMap
zip to the get the resultant future and then flatMap to create the result.
recover in case the zipped future fails.
def handleFormSubmission = silhouette.SecuredAction.async { implicit request =>
val userFuture = userService.findOneByUserId(userId)
val avatarFuture = avatarService.findOneByUserId(userId)
userFuture.zip(avatarFuture).flatMap { case (user, avatar) =>
myForm.bindFromRequest.fold(
formWithErrors => formWithErrorsBranch(formWithErrors, user, avatar),
changeData => changeDataBranch(changeData, user, avatar))
}.recover { case th =>
Ok("error occurred because: " + th.getMessage)
}
}
this thread gave me an idea how to structure my code: Scala-way to handle conditions in for-comprehensions?
The part in question:
// First create the JSON
val resultFuture: Future[Either[Failure, JsResult]] = for {
userRes <- userDao.findUser(userId)
user <- userRes.withFailure(UserNotFound).right
authRes <- userDao.authenticate(user)
auth <- authRes.withFailure(NotAuthenticated).right
goodRes <- goodDao.findGood(goodId)
good <- goodRes.withFailure(GoodNotFound).right
checkedGood <- checkGood(user, good).right
} yield renderJson(Map("success" -> true)))
This are the lines I do not understand:
user <- userRes.withFailure(UserNotFound).right
authRes <- userDao.authenticate(user)
The userRes.withFailure(UserNotFound).right is mapped to userDao.authenticate(user). This will create a new Either with a Future on its right, correct?
How can
val resultFuture: Future[Either[Failure, JsResult]]
be of its type. I think instead of a JsResult there should be another future.
Can anyone explain this to me?
EDIT: Since cmbaxter and Arne Claassen confirmed this, the new question is: How should I write this code, so it does not look ugly, but clean and structured?
I believe the answer you received needlessly mixed Either's into the mix when Future's are already perfectly capable of communicating failure. The main thing you were missing was a way to get from an Option to the option's value without explicitly throwing exceptions.
I would suggest that you change the Failures object to the following:
object Failures {
sealed trait Failure extends Exception
// Four types of possible failures here
case object UserNotFound extends Failure
case object NotAuthenticated extends Failure
case object GoodNotFound extends Failure
case object NoOwnership extends Failure
// Put other errors here...
// Converts options into Futures
implicit class opt2future[A](opt: Option[A]) {
def withFailure(f: Failure) = opt match {
case None => Future.failed(f)
case Some(x) => Future.successful(x)
}
}
}
Now you can map a Future[Option[A]] to a Future[A] and specify the failure condition, resulting in a for comprehension like this:
def checkGood(user: User, good: Good) =
if (checkOwnership(user, good))
Future.successful(good)
else
Future.failed(NoOwnership)
val resultFuture: Future[JsResult] = for {
userOpt <- userDao.findUser(userId)
user <- userOpt.withFailure(UserNotFound)
authOpt <- userDao.authenticate(user)
auth <- authOpt.withFailure(NotAuthenticated)
goodOpt <- goodRes.withFailure(GoodNotFound)
checkedGood <- checkGood(user, good)
} yield renderJson(Map("success" -> true))))
Now that you have a Future[JsResult] you can map the failed scenarios to your desired output and the success scenario is just the JsResult. Hopefully you are using this in an asynchronous framework which expects you to feed it a future and has its own failed future to error response mapping (such as Play!).
According to the Scala Language Specification (§6.19), "An enumerator sequence always starts with a generator". Why?
I sometimes find this restriction to be a hindrance when using for-comprehensions with monads, because it means you can't do things like this:
def getFooValue(): Future[Int] = {
for {
manager = Manager.getManager() // could throw an exception
foo <- manager.makeFoo() // method call returns a Future
value = foo.getValue()
} yield value
}
Indeed, scalac rejects this with the error message '<-' expected but '=' found.
If this was valid syntax in Scala, one advantage would be that any exception thrown by Manager.getManager() would be caught by the Future monad used within the for-comprehension, and would cause it to yield a failed Future, which is what I want. The workaround of moving the call to Manager.getManager() outside the for-comprehension doesn't have this advantage:
def getFooValue(): Future[Int] = {
val manager = Manager.getManager()
for {
foo <- manager.makeFoo()
value = foo.getValue()
} yield value
}
In this case, an exception thrown by foo.getValue() will yield a failed Future (which is what I want), but an exception thrown by Manager.getManager() will be thrown back to the caller of getFooValue() (which is not what I want). Other possible ways of handling the exception are more verbose.
I find this restriction especially puzzling because in Haskell's otherwise similar do notation, there is no requirement that a do block should begin with a statement containing <-. Can anyone explain this difference between Scala and Haskell?
Here's a complete working example showing how exceptions are caught by the Future monad in for-comprehensions:
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Try, Success, Failure}
class Foo(val value: Int) {
def getValue(crash: Boolean): Int = {
if (crash) {
throw new Exception("failed to get value")
} else {
value
}
}
}
class Manager {
def makeFoo(crash: Boolean): Future[Foo] = {
if (crash) {
throw new Exception("failed to make Foo")
} else {
Future(new Foo(10))
}
}
}
object Manager {
def getManager(crash: Boolean): Manager = {
if (crash) {
throw new Exception("failed to get manager")
} else {
new Manager()
}
}
}
object Main extends App {
def getFooValue(crashGetManager: Boolean,
crashMakeFoo: Boolean,
crashGetValue: Boolean): Future[Int] = {
for {
manager <- Future(Manager.getManager(crashGetManager))
foo <- manager.makeFoo(crashMakeFoo)
value = foo.getValue(crashGetValue)
} yield value
}
def waitForValue(future: Future[Int]): Unit = {
val result = Try(Await.result(future, Duration("10 seconds")))
result match {
case Success(value) => println(s"Got value: $value")
case Failure(e) => println(s"Got error: $e")
}
}
val future1 = getFooValue(false, false, false)
waitForValue(future1)
val future2 = getFooValue(true, false, false)
waitForValue(future2)
val future3 = getFooValue(false, true, false)
waitForValue(future3)
val future4 = getFooValue(false, false, true)
waitForValue(future4)
}
Here's the output:
Got value: 10
Got error: java.lang.Exception: failed to get manager
Got error: java.lang.Exception: failed to make Foo
Got error: java.lang.Exception: failed to get value
This is a trivial example, but I'm working on a project in which we have a lot of non-trivial code that depends on this behaviour. As far as I understand, this is one of the main advantages of using Future (or Try) as a monad. What I find strange is that I have to write
manager <- Future(Manager.getManager(crashGetManager))
instead of
manager = Manager.getManager(crashGetManager)
(Edited to reflect #RexKerr's point that the monad is doing the work of catching the exceptions.)
for comprehensions do not catch exceptions. Try does, and it has the appropriate methods to participate in for-comprehensions, so you can
for {
manager <- Try { Manager.getManager() }
...
}
But then it's expecting Try all the way down unless you manually or implicitly have a way to switch container types (e.g. something that converts Try to a List).
So I'm not sure your premises are right. Any assignment you made in a for-comprehension can just be made early.
(Also, there is no point doing an assignment inside a for comprehension just to yield that exact value. Just do the computation in the yield block.)
(Also, just to illustrate that multiple types can play a role in for comprehensions so there's not a super-obvious correct answer for how to wrap an early assignment in terms of later types:
// List and Option, via implicit conversion
for {i <- List(1,2,3); j <- Option(i).filter(_ <2)} yield j
// Custom compatible types with map/flatMap
// Use :paste in the REPL to define A and B together
class A[X] { def flatMap[Y](f: X => B[Y]): A[Y] = new A[Y] }
class B[X](x: X) { def map[Y](f: X => Y): B[Y] = new B(f(x)) }
for{ i <- (new A[Int]); j <- (new B(i)) } yield j.toString
Even if you take the first type you still have the problem of whether there is a unique "bind" (way to wrap) and whether to doubly-wrap things that are already the correct type. There could be rules for all these things, but for-comprehensions are already hard enough to learn, no?)
Haskell translates the equivalent of for { manager = Manager.getManager(); ... } to the equivalent of lazy val manager = Manager.getManager(); for { ... }. This seems to work:
scala> lazy val x: Int = throw new Exception("")
x: Int = <lazy>
scala> for { y <- Future(x + 1) } yield y
res8: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#fedb05d
scala> Try(Await.result(res1, Duration("10 seconds")))
res9: scala.util.Try[Int] = Failure(java.lang.Exception: )
I think the reason this can't be done is because for-loops are syntactic sugar for flatMap and map methods (except if you are using a condition in the for-loop, in that case it's desugared with the method withFilter). When you are storing in a immutable variable, you can't use these methods. That's the reason you would be ok using Try as pointed out by Rex Kerr. In that case, you should be able to use map and flatMap methods.
When inserting a value into a persistence layer and returning the result object it is usually a good practice to fetch the newly created entity instead of returning the input data again.
When I try to do this in Scala using reactivemongo I stumble over my language skills.
def create(user: User): Future[User] = {
val newUser = user.createOID()
collection.insert(newUser).map {
case ok if ok.ok => {
for {
createdUser <- this.findOne(BSONDocument("_id" -> newUser._id))
} yield {
createdUser match {
case None => throw new RuntimeException("Could not find just created user")
case Some(x) => x
}
}
}
case error => throw new RuntimeException(error.message)
}
}
Where findOne has the signature:
def findOne(query: BSONDocument): Future[Option[User]]
I get the following error:
[error] found : scala.concurrent.Future[models.User]
[error] required: models.User
[error] createdUser <- this.findOne(BSONDocument("_id" -> newUser._id))
[error] ^
If I return the newUser object everything is fine.
I think I have a general misunderstanding what is happening here - maybe there is a better way to fetch the created object in one shot.
I would say that idiomatic Play/Scala way to do that is the following
def create(user: User): Future[Option[User]] = {
val newUser = user.createOID()
for {
nu <- collection.insert(newUser)
createdUser <- findOne(BSONDocument("_id" -> newUser._id))
} yield {
createdUser
}
}
Notice that this does return Future[Option[User]] and not Future[User] as in your code. I believe that Option[User] is definitely the way to go in this case as it actually tells clients of this method that it's not guaranteed that insertion will succeed (and thus runtime exception is not required as client will do .map on the result of this method — avoid using exceptions if you can deal with them gracefully).
You might also check nu for being ok within yield.