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.
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 found the following code and I'm not sure how it works. This is Scala code with Play framework.
## route file ##
GET /:object #controllers.ResultsController.resultController(object)
## ResultsController file ##
def resultController(object: SomeObject) = {
getResult(object)
}
def private getResult(object: SomeObject): Result = {
lazy val result = computeResult(object) match {
case Some(response) => JsonOk(response)
case None => JsonInternalError(...)
}
result
}
I'm not sure when result is evaluated.
I mean, the return is something that must be evaluated when used, or is it resolved at the time of return?
The lazy characteristic leaves the context of the function?
In this case, the value is never used, only returned as result of a GET request.
Thanks a lot!!!
Yes, the lazy result is evaluated inside getResult to be returned. Result - the return type of your getResult is not lazy and actually you can't define function return type as lazy. If for some reason you really need that computation to be lazy, it probably should be something like () => Result or Future[Result].
Also the idea that "In this case, the value is never used, only returned as result of a GET request." is clearly wrong. The browser doesn't understand Scala, it understands HTTP which is a text format. It means that somewhere under the hood the framework has to convert your Result into a text form (i.e. serialize it) and it will definitely require evaluating it anyway.
I added line numbers to natural explanations.
1 def private getResult(object: SomeObject): Result = {
2 lazy val result = computeResult(object) match {
3 case Some(response) => JsonOk(response)
4 case None => JsonInternalError(...)
5 }
6 result
7 }
It resolves on the line 6. So it returns the actual value from the method getResult
The lazy use synchronized lock inside. lazy always verify if the variable already resolved.
The result variable is local and always used and used only once. So no sense in lazy.
In your example, according to the points "2" and "3" lazy slows down the program. It also can lead to potential deadlocks on lines 2, 3, 4. For more datils look the "Scenario 3: Deadlock in combination with synchronization" in this article https://blog.codecentric.de/en/2016/02/lazy-vals-scala-look-hood/.
My advice is to remove the lazy here.
I am writting a scala function which follows the following workflow:
take an id as parameter.
use the id to get a object from mongo database. Here I am using reactive mongo.
after getting the object make another query to the database to get a list of items and return it.
I have implemented this workflow in the following way:
def functionA(id:String):Future[List[Hotel]]={
var futureHotel = hotelRepository.getHotel(id) // returns Futue[Option[Hotel]]
val result = for {
r<-futureHotel
}yield (hotelRepository.getHotels(r.get.giataid)) // this is supposed to be Future[List[Hotel]]
}
It gives me error message like: Type mismatch. Found Unit require Future[List[Hotel]]
As I am new to Scala, I am sure I am missing something. Will be cool if someone can point out. Any clue or help will be appreciated.
You are assigning the result to result. Assignment in scala is of type Unit (doesn't return anything). Remove the assignment, since the last expression becomes the return value.
Also, if the getHotels method returns Future[List[...]] you'll end up with Future[Future[List[...]]. You probably want something like this:
def functionA(id:String):Future[List[Hotel]]= {
for {
r <- hotelRepository.getHotel(id)
result <- hotelRepository.getHotels(r.get.giataid)
} yield result
}
This gets translated to a call to flatMap that doesn't produce the doubly nested future.
I have the following piece of code that I am trying to enhance:
I am using the java.nio.file package to represent a directory or a file as a Path.
So here goes:
import java.nio.file.{Paths,DirectoryStream,Files,
Path,DirectoryIteratorException}
val path: Path = Paths.get(directoryPath)
var directoryStream: Option[DirectoryStream[Path]] = None
// so far so good
try {
directoryStream = Some(Files.newDirectoryStream(pathO))
// this is where i get into trouble
def getMeDirStream: DirectoryStream[Path] =
if (!directoryStream.isEmpty && directoryStream.isDefined)
getMeDirStream.get
else
None
// invoke the iterator() method of dstream here
}
The above piece of code will not compile because I do not know what to return in the else, and right now, for the life of me, I can only come up with None, which the compiler simply does not like and I would like to learn what should be its replacement.
I want this example to be a learning lesson of Option and Some for me.
Okay, this is where I choke. I would like to check if the directoryStream is not empty and is defined, and then if this is the case, I would like to invoke getMeDirStream.get to invoke the iterator() method on it.
The API for Option tells me that invoking the get() method could result in a java.util.NoSuchElementException if the option is empty.
If the directoryStream is empty I want to return something and not None, because IntelliJ is telling me that "Expression of type None.type doesn't conform to expected type DirectoryStream[Path]".
Now, I am being all naive about this.
I would like to know the following:
What should I return in the else other than None?
Should I wrap the getMeDirStream.get in a try-catch with a java.util.NoSuchElementException, even though I am checking if the directoryStream is empty or not.?
What is the purpose of a try-catch in the getMeDirStream.get, if there is indeed such a need?
How can I clean up the above piece of code to incorporate correct checks for being isDefined and for catching appropriate exceptions?
Once I know what to return in the else (and after putting in the appropriate try-catch block if necessary), I would like to invoke the iterator() method on getMeDirStream to do some downstream operations.
Some and None are subtypes of Option, but to be more correct, they are actually two different cases of Option or data constructors. In other words, even though Scala allows you to directly invoke a Some or a None you should still regard their type to be Option. The more important thing to take from this is that you should never under any circumstance invoke Option#get as it is unsafe.
The intention of Option is to indicate the possibility that a value does not exist. If you care about the errors, then you should probably look at using Either instead (or Scalaz's Either called \/).
You can keep the computation within the Option context and then only extract the value later, or provide a default.
def fromTryCatch[A](a: => A): Either[Throwable, A] = try { Right(a) } catch { case e: Throwable => Left(e) }
val getMeDirStream: Option[java.util.Iterator[Path]] =
for {
path <- fromTryCatch(Paths.get(directoryPath)).toOption
directoryStream <- fromTryCatch(Files.newDirectoryStream(pathO)).toOption
} yield directoryStream.iterator()
Later, or right after, you can get the iterator, or provide a default value:
val iterator = getMeDirStream.getOrElse(java.util.Collections.emptyIterator[Path])
Your specific questions are difficult to address because it's unclear exactly what you're trying to achieve. In particular, when you ask what the purpose of the try block is... Well, you wrote it, so only you can answer that.
In general, you never call get on an Option. You either use pattern matching:
option match {
case Some(value) => /* ... */
case None => /* ... */
}
or you use methods like map, flatMap, and foreach (or the equivalent comprehension syntax that gpampara's code uses).
My revision of gpampara's answer:
import scala.collection.convert.wrapAll._
import scala.util.Try
import java.nio.file.{Paths, Files, Path}
val getMeDirStream: Option[Iterator[Path]] =
for {
path <- Try(Paths.get("")).toOption
directoryStream <- Try(Files.newDirectoryStream(path)).toOption
} yield directoryStream.iterator
Changes:
Using Try(...).toOption instead of Either
Using implicits in scala.collection.convert to return the result as a Scala Iterator.
Try is similar to Option. Instead of Some and None, it has Success and Failure subtypes, and the failure case includes a Throwable, whereas None is just a singleton with no additional information.
Why does queue.get() return empty list?
class MyQueue{
var queue=List[Int](3,5,7)
def get(){
this.queue.head
}
}
object QueueOperator {
def main(args: Array[String]) {
val queue=new MyQueue
println(queue.get())
}
}
How i can get first element?
It's not returning the empty list, it's returning Unit (a zero-tuple), which is Scala's equivalent of void in Java. If it were returning the empty list you'd see List() printed to the console rather than the () (nullary tuple).
The problem is you're using the wrong syntax for your get method. You need to use an = to indicate that get returns a value:
def get() = {
this.queue.head
}
Or this is probably even better:
def get = this.queue.head
In Scala you usually leave off the parentheses (parameter list) for nullary functions that have no side-effects, but this requires you to leave the parentheses off when you call queue.get as well.
You might want to take a quick look over the Scala Style Guide, specifically the section on methods.
Sometimes it can be good to use
take 1
instead of head because it doesnt cause an exception on empty lists and returns again an empty list.