def getRecordsFromDB(primaryKey: Long): Future[Seq[Record]] = ???
def extractAValueFromResult: Future[String] =
getRecordsFromDB("abcxyz").map(_.headOption.map(_.fieldName).recover(????)
Have the above functions - first one to query DB and get records and the second one to extract a particular field from the records returned by first one.
How should I modify the extractAValueFromResult so I get Future[String], so when the records are empty or when the Future failed, I can return empty String? Fyi, just have to log extractAValueFromResult's return value, so do not want to fail the whole application when the Future fails.
This does what you want:
getRecordsFromDB("abcxyz").map(_.head).map(_.fieldName).recover { case _ => "" }
A couple of things about it:
_head is "code smell" (in the same way Option.get is), because it will throw when the collection is empty. A "purist" would have written _.headOption.fold("")(_.fieldName) instead ... but since you are going to catch/recover
in the end anyway, I don't quite see the point.
catching all exceptions is actually "code smell" too. It should at least be case NonFatal(_) => ... (I just wrote _ for brevity), but even then, it is rarely a good idea: a failure due to a network timeout or a database being down is a very different thing from the entry not being there. Silently collapsing all failures together, and then also conflating them with "not found" condition is just ... wrong. If you are just playing with things, it'll work, but don't ever do this in any production code.
You could do something like this:
def extractAValueFromResult: Future[String] =
getRecordsFromDB("abcxyz")
.flatMap(seq => Future.fromTry(Try(seq.headOption.map(_.fieldName).get)))
Related
I have a db query which returns a Future[String], the implementation does not matter, but the signature is something like this:
def getTicketType(id: Long): Future[String] = {...}
And imagine I have a list of ids which i would want to retrieve ticket types from those ids. so something like this:
val listOfIds: List[Long] = ... (from somewhere else of the code)
val ticketTypesFuture: Future[List[String]] = Future.sequence(listOfIds.map(getTicketType))
So far so good, but there is another function, which is called within the main process, that HAS to return a Boolean or an Option[Boolean] value, since it's result is used in the main process which holds a gigantic for comprehension, combined of some Either[Int, Option[JsValue]]'s. The way I'm doing it right now (which I believe is the worst way of implementing such thing :) ), is this:
def thatFunction(): Boolean = {
// ... val listOfIds, ticketTypesFuture defined above
var result = false // here is the nasty code :)
val futureResult: Future[Boolean] = ticketTypesFuture.map { ticketTypes =>
if (!ticketTypes.forall(someCondition)) {
// some code which returns either true or false
} else false
}
futureResult.omComplete {
case Success(value) => result = value
case _ => result = false
}
result
}
But there must be a better approach to do this, so I would appreciate any help!
The sane option is to go the other way and make your "gigantic for comprehension, combined of some Either[Int, Option[JsValue]]'s" work with futures. Wrap the part before and after the query using Future.apply or Future.successful, and you should be fine. Or if it contains other database/API accesses, make them return Future as well.
If you can't, your choice is:
use Await.result as in Tim's answer, which loses any benefit of futures. If you really want that, consider using a library which doesn't return a future in the first place. But this may be a placeholder until you switch.
use Future#value if you want not to wait and just do something else if the result is not ready. For example you might show some old results, or an empty list until you get data.
(After writing this, I saw #jwvh already said basically the same in a comment, hopefully it still helps to have a more expanded version.)
If you must convert Future[Boolean] to Option[Boolean] then you need to wait for the Future using Await.result. This will throw an error if the Future fails, so wrap it in a Try.
val futureResult: Future[Boolean] = ???
Try(Await.result(futureResult, Duration.Inf)).toOption
But the better solution is to convert the calling code to accept a Future and avoid blocking.
As you can see in the answers and comments, multiple approaches have been discussed, which include:
1- waiting for the Future to complete (using mutation, Await, ...) which are the worst of the approaches, so just don't do that :)
2- mapping on the value like this: futureResult.value.map(t => t.isSuccess && t.get)
and some other solutions.
As #jwh mentioned, another solution is to handle it properly, anything that touches futureResult becomes a Future!
But since I couldn't change all the calculations and functions inside that for comprehension, I placed this Future[Boolean] condition outside of the for loop, and everythin is just fine.
i have the following for comprehension. It is supposed to delete a row in my database but only if the row exists (So if there is a news for the given id):
override def deleteNews(newsId: Long): Int = {
val getAndDelete = for {
Some(news) <- newsDao.get(newsId)// returns Future[Option[News]]
delete <- newsDao.remove(news) // returns Future[Int]
} yield delete
Await.result(getAndDelete, responseTimeout)
}
But i don't know how to handle the case when there is no element for a given id. Currently this exception is thrown:
Unexpected exception[NoSuchElementException: Future.filter predicate is not satisfied]
I hope my approach is not to awful :D
I'm relatively new to scala.
Using Await is not that great of an idea: it's best to delay the blocking as long as you possibly can.
IMO, no element for a given ID shouldn't be a failure. newsDao.get should return a successful future of None if there's nothing with that ID, you shouldn't call newsDao.remove on an ID which doesn't exist if you can help it, and the overall result should just be successfully deleted zero rows (as I'd look at the contract of deleteNews as ensuring that at some point between the call and the return there were no rows associated with newsId (a little bit of handwaving here around data races, of course...)).
So with that, assuming you can't change newsDao's implementation:
val getFut: Future[Option[News]] =
newsDao.get(newsId).recover {
// can still fail for other reasons
case _: NoSuchElementException => None
}
// I really prefer map/flatMap directly vs. for-comprehension sugar, especially when dealing with multiple monadicish things
// Not the most succinct, but leaving meaningful names in for documentation
val getAndRemove =
getFut.flatMap { newsOpt =>
newsOpt.map { news =>
newsDao.remove(news)
}.getOrElse(Future.successful(0))
}
If you still need deleteNews to return a bare Int, you can Await.result and accept that you'll sometimes get exceptions thrown and that this is probably suboptimal.
As Levi mentioned, always try to avoid blocking and when you pattern match, make sure you handle all the cases.
You can do this using for-comprehension like below:
def deleteNews(newsId: Long): Future[Option[Int]] =
for {
news <- newsDao.get(newsId)
delete <- Future.sequence(news.map(id => newsDao.remove(id)).toList)
} yield delete.headOption
Honestly I have not used this trick to go from Option[Future] to Future[Option]. I would be interested to see what others says!
I am using scanamo to query a dynamodb and all i want to do is check that the db actually exists. I'm not really concerned with the record I get back, just that there were no errors. For the query part I'm using this:
trait DynamoTestTrait extends AbstractDynamoConfig { def test(): Future[List[Either[DynamoReadError, T]]] = ScanamoAsync.exec(client)table.consistently.limit(1).scan())}
that returns the Future List. I want to evaluate the first? item in the list and just return true if it is not a read error.
I thought this would work but it doesn't:
val result = test() match {
case r: DynamoReadError => Future.successful(false)
case r: Registration => Future.successful(true)
}
I'm new to scala so struggling with return types and things. This is a Play api call so i need to evaluate that boolen future at some point. With something like this:
def health = Action {
val isHealthy = h.testDynamo()
val b: Boolean = Await.result(isHealthy, scala.concurrent.duration.Duration(5, "seconds"))
Ok(Json.toJson(TestResponse(b.toString)))
}
I think this is probably wrong also as i don't want to use Await but i can't get async to work either.
Sorry, i'm kind of lost.
When i try to evaluate result i only get a message about the Future:
{
"status": 500,
"message": "Future(<not completed>) (of class scala.concurrent.impl.Promise$DefaultPromise)"
}
The result is a Future so you can't test the result without doing something like Await.result (as you do later). What you can do is modify the result returned by the Future to be the result you need.
In your case you can do this:
test().map(_.headOption.forall(_.isRight))
This will return Future[Boolean] which you can then use in your Await.result call.
Here is how it works:
map calls a function on the result of the Future, which is type List[Either[DynamoReadError, T]] and returns a new Future that gives the result of that function call.
_.headOption takes the head of the list and returns an Option[Either[DynamoReadError, T]]. This is Some(...) if there are one or more elements in the list, or None if the list is empty.
forall checks the contents of the Option and returns the result of the test on that option. If the Option is None then it returns true.
_.isRight tests the value Either[...] and returns true if the value is Right[...] and false if it is Left[...].
This does what you specified, but perhaps it would be better to check if any of the results failed, rather than just the first one? If so, it is actually a bit simpler:
test().map(_.forall(_.isRight))
This checks that all the entries in the List are Right, and fails as soon as a Left is found.
The problem with returning this from Play is a separate issue and should probably be in a separate question.
There's a function in our codebase with a signature like this:
def hasPermission(...): Try[Unit]
It basically checks if a user has permission to perform a certain action on a certain item. If the user has permission, it returns an empty Success, and if not, it returns a Failure with a specific exception type. This function is often used within comprehensions like this:
for {
_ <- hasPermission(...)
res <- doSomething()
} yield res
This seems like bad practice, but I can't quite articulate why I feel that way. To me, it seems like hasPermission should simply return a Boolean.
Is this an appropriate use of a Try?
edit: I think my question is different than the linked one because it's more specific. That one is asking a general question about returning Try[Unit], which I believe is acceptable in some cases.
If the method says hasPermission, then I'd say it should return a Boolean, or a Try[Boolean]. Try[Unit] is not as obvious as Try[Boolean], and the caller would have to inspect the exception to tell if it didn't have the permission, or whether it failed to retrieve the permission info.
Now that said, generally calling hasPermission and then acting depending on the result can cause race conditions (e.g. if the permission is revoked after hasPermission is called). Therefore it's often preferable to do def doSomething(...): Try[Unit] and then raise e.g. a NoPermissionException.
Generally, the use of exceptions for control flow is an anti-pattern
Try tries (no pun intended) to encapsulate that flow control, but if you don't actually need to use exceptions, there's no reason to. Scala 2.12's Either implementation seems close to what you probably want:
Either is right-biased, which means that Right is assumed to be the default case to operate on. If it is Left, operations like map and flatMap return the Left value unchanged:
Let's assume that you are implementing a web server, and this logic is in control of a particular path:
type Request = ...
type Response = String
type ResponseOr[+T] = Either[Response, T]
def checkPermission: ResponseOr[Unit] =
if(hasPermission) Right(())
else Left("insufficient permissions")
def doSomething(req: Request): ResponseOr[Something] =
if(argumentsAreBad(req)) Left("arguments are bad!")
else Right(new Something)
def makeResponse(result: Something): Response = ???
def handleIt(req: Request): Response = {
val result = for {
_ <- checkPermission
result <- doSomething
} yield makeResponse(result)
result.merge // special method for `Either[T, T]` that gives a `T`
}
You'll see similar behavior to Success vs Failure - think of Left as analagous to a Failure, where if at any point one of the flatMap/map steps returns a Left, that's the end result and the rest are "skipped". No exceptions required.
Given an Option, what is the idiomatic way to get its value or throw an exception trying?
def foo() : String = {
val x : Option[String] = ...
x.getOrException()
}
A throw "statement" is really an expression in Scala, and it has type Nothing, which is a subtype of every other type. This means you can just use plain old getOrElse:
def myGet[A](oa: Option[A]) = oa.getOrElse(throw new RuntimeException("Can't."))
You really, really shouldn't be doing this, though.
(EDIT: this is not the best or most idiomatic way to do it. I wrote it when I was not familiar with Scala. I leave it here for an example of how not to do it. Nowadays I would do as #TravisBrown)
I think it really boils down to two things:
how sure are you that the value is there?
how do you want to react if it isn't?
If at that point in your code you expect the value to be there, and in the remote case that it isn't you want your program to fail fast, then I would only do a normal get and let Scala throw a NoSuchElementException if there was no value:
def foo() : String = {
val x : Option[String] = ...
x.get
}
If you want to handle the case differently (throw your own exception) I think a more elegant way would look like this:
def foo(): String = {
val x: Option[String] = None
x match {
case Some(value) => value
case None => throw new MyRuntimeException("blah")
}
}
And of course if you want to supply your own alternative value for the case that the Option is None you would just use getOrElse:
def foo(): String = {
val x: Option[String] = None
x.getOrElse("my alternative value")
}
I hope this will help you to understand how to represent errors (and generally effects) using types.
Error handling strategies in functional Scala
Use Option to return optional values. For example - fail to find entity in storage.
Use Option(possiblyNull) to avoid instances of Some(null).
Use Either[Error, T] to report expected failure. For example - email format is wrong, cannot parse a string to a number, etc.
Model your errors as ADTs (simply speaking kind of type hierarchies) to use it, for example, on the Left of the Either to represent more complex error scenarios.
Throw Exception only to signal unexpected and not-recoverable failures. Like missing config file.
Use Either.catchOnly or Try or Cats.IO (advanced) rather than a catch block for handling unexpected failures. Hint: You can still use ADT but extend them from throwables. More about Either vs Try.
Use Validated data-type from Cats lib to accumulate errors rather than fail-fast (Either), but prefer Either's on module-level to simplify the composition of the program (to have the same types). For example - form data validation, parsing errors accumulation.
Use mentioned types and don't optimize program preemptively - since most probably, bottle-necks would be in business logic, not in effect types.
Such an approach will simplify maintenance and updates of your code since you can reason about it without going to implementation specifics (aka local-reasoning). Also - reduce bugs - you cannot miss an error in the type. And compose the program easier (with help of map, flatMap and other combinators) - since it's simpler on type level, rather than with non-local exceptions and side-effects.
More about learning functional Scala.
But be aware that sometimes with this approach types could stack up and it could become harder to compose things. Given, for example: x: Future[Either[Error, Option[T]]] What you can do:
Use map and flatMap in combination with pattern-matching to compose different values of such types, for example:
x.faltMap { case Right(Some(v)) => anotherFuture(v); case Left(er) => ... }
If it doesn't help you can try to use MonadTransformers (don't be scared of the name, it's just wrappers around the effect types like Either and Future)
Also, an option is to simplify your errors ADT by extending them from the Throwable to unify it with Future, then it'll be Future[Option[T]]
And finally, in your case one option will be:
def foo() : Either[Error, String] = {
val x : Option[String] = ...
x match {
case Some(v) => Right(v)
case None => Left(Error(reason))
}
}
Just use the .get method.
def get[T](o:Option[T]) = o.get
It will throw a NoSuchElementException if o is an instance of None.
Basically, I would work with options like this:
def addPrint(oi:Option[Int]) = oi.map(_+1).foreach(println)
addPrint(Some(41))
addPrint(Some(1336))
addPrint(None)
to avoid your specific question.
Scala now support this operation on maps using getOrElse() method, see documentation here
As pointed out already, throwing an exception in Scala is an expression as well.
So you can do the following:
myMap.getOrElse(myKey, throw new MyCustomException("Custom Message HERE")