Hi i have a question regarding validating a List in Scala. I have a method that looks somewhat like this:
def validate(item: Item): Try[Unit] = {
if (item.isValid) Success(())
else Failure(new isNotValidException("not valid"))
}
Now i am using this method to validate a itemList: List[Item] as follow:
def listIsValid(list: List[Item]): Try[Unit] = {
list
.map(validate(_))
.collectFirst{ case f # Failure(_: Exception) => f }
.getOrElse(Success(()))
}
Which i ultimately want to resolve to a single Try[Unit] (either Success if all items are valid, or a Failure if at least one item is not valid.
Is this a good way to validate all items in a List? Or is there a better way to validate items in lists? It should fail fast and if one item fails i don't need to know if other items are invalid at the time. The list validation is used in a for comprehension and ultimately needs to resolve to a single Try[Unit] again
i would use the method exists on Lists, and write this method as follows :
def listIsValid(list: List[Item]): Boolean = {
list.exists(!_.isValid)
}
If you want to check if every element in a sequence fulfills a predicate you use forall:
list.forall(_.isValid)
For more details check the forall documentation
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.
I want to flatMap a Try[Option[A]] using some function that uses the value inside the Option to create another Try, and I want the solution to be simple and idiomatic. I have illustrated the problem with an example. The goal is to create a Option[Group] with members and events wrapped in a single Try that can contain errors from any of the three functions.
def getGroup(id: Long): Try[Option[Group]]
def getMembersForGroup(groupId: Long): Try[Seq[Member]]
def getMeetingsForGroup(groupId: Long): Try[Seq[Meeting]]
I find it difficult to flatMap from the Try returned by getGroup to the Try from the member- and meeting-functions because there's an Option "in the way". This is what i have come up with so far:
getGroup(id).flatMap(
groupOpt => groupOpt.map(
group => addStuff(group).map(group => Some(group))
).getOrElse(Success(None))
)
def addStuff(g: Group): Try[Group] =
for {
members <- getMembersForGroup(g.id)
meetings <- getMeetingsForGroup(g.id)
} yield g.copy(members = members, meetings = meetings)
What I don't like about my solution is that I have to wrap the group returned by addStuff in an Option to perform the getOrElse. At this point the type is Option[Try[Option[Group]]] which I think makes the solution difficult to understand at first glance.
Is there a simpler solution to this problem?
Cats has an OptionT type that might simplify this: documentation here and source here.
Your example would be:
def getGroupWithStuff(id: Long): OptionT[Try, Group] = {
for {
g <- OptionT(getGroup(id))
members <- OptionT.liftF(getMembersForGroup(g.id))
meetings <- OptionT.liftF(getMeetingsForGroup(g.id))
} yield g.copy(members = members, meetings = meetings)
}
You could use .fold instead of .map.getOrElse ... That makes it a little bit nicer:
getGroup(id)
.flatMap {
_.fold(Try(Option.empty[Group])){
addStuff(_).map(Option.apply)
}
}
or write the two cases explicitly - that may look a little clearer in this case, because you can avoid having to spell out the ugly looking type signature:
getGroup(id).flatMap {
case None => Success(None)
case Some(group) => addStuff(group).map(Option.apply)
}
You probably could simplify your getGroup call to:
getGroup(id).map(
groupOpt => groupOpt.flatMap(
group => addStuff(group).toOption
)
)
, however that would be at cost of ignoring potential failure info from addStuff call. If it is not acceptable then it is unlikely you could simplify your code further.
Try this. You get to keep your for comprehension syntax as well as Failure information from any of the three calls (whichever fails first).
def getFullGroup(id: Long): Try[Option[Group]] =
getGroup(id).flatMap[Option[Group]] { _.map[Try[Group]]{ group =>
for {
meetings <- getMeetingsForGroup(id)
members <- getMembersForGroup
} yield group.copy(meetings = meetings, members = members)
}.fold[Try[Option[Group]]](Success(None))(_.map(Some(_)))
}
Note the type acrobatics at the end:
fold[Try[Option[Group]]](Success(None))(_.map(Some(_)))
It's hard to get right without type annotations and an IDE. In this particular case, that's not too bad, but imagine meetings and members depended on another nested try option which in turn depended on the original. Or imagine if you wanted to a comprehension on individual Meetings and Groups rather than using the entire list.
You can try using an OptionT monad transformer from cats or scalaz to stack Try[Option[Group]] into a non-nested OptionT[Try, Group]. If you use a monad transformer, it can look like this:
def getFullGroup(id: Long): OptionT[Try, Group] =
OptionT(getGroup(id)).flatMapF { group =>
for {
meetings <- getMeetingsForGroup(id)
members <- getMembersForGroup(id)
} yield group.copy(meetings = meetings, members = members)
}
}
For this particular case, there's not really much gain. But do look into it if you have a lot of this kind of code.
By the way, the boilerplate at the end of the first example that flips the Try and Option is called a sequence. When it follows a map, the whole thing is called traverse. It's a pattern that comes up often and is abstracted away by functional programming libraries. Instead of using OptionT, you can do something like:
def getFullGroup(id: Long): Try[Option[Group]] =
getGroup(id).flatMap[Option[Group]] { _.traverse { group =>
for {
meetings <- getMeetingsForGroup(id)
members <- getMembersForGroup
} yield group.copy(meetings = meetings, members = members)
}
}
(Generally, if you're mapping f then flipping monads, you want to traverse with f.)
In Scala I have a List with an optional Option. This arises for example when you use for comprehension on a List and your yield returns an Option. In my case I was processing a JSON object and using for comprehension on the list of fields (List[JField]).
What's the best way to open up the list and map List() to None and List(Some(a)) to Some(a)?
A first approach would be
def headOrNone[A](list:List[Option[A]]) =
list match {
case Nil => None
case a::Nil => a
}
Another approach
def headOrNone[A](list:List[Option[A]]) = list.headOption.getOrElse(None)
A third approach (a variation on the headOption implementation)
def headOrNone[A](list:List[Option[A]]) = if (list.isEmpty) None else list.head
I personally prefer the third approach. Is there a better name for this function than headOrNone and what is the idiomatic scala way to write it?
You're solving a problem that probably shouldn't have been created. Instead, you probably want
for (x <- list) yield f(x) // Yields Option
to be
list.flatMap(f)
and then you'll have either zero or one things in your list to begin with (which you can extract using headOption).
How about this:
def headOrNone[A](list: List[Option[A]]) = list.flatten.headOption
headOrNone(List(Some(4))) // Some(4)
headOrNone(List()) // None
Though the first choice has the advantage of giving you an error if you happen to have list with more than one item, which, according to your description, seems like an error condition.
But personally, I would re-evaluate the code that produces the List[Option[A]] and see if there's a way to just have it return the right thing in the first place!