Is it good practice to use a Try for flow control? - scala

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.

Related

Better approach to convert Future to Boolean

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.

How to handle a failed future in a for-comprehension

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!

Use Future.successful(None) vs Future.failed(new Exception()) for an API that may return no results?

What is the idiomatic way in Scala to type an API that may or may not return results?
I can simply type to Future[Option[A]] and return a None if there is no results, but this will make things a little more verbose for API clients I think because it has to do some pattern matching (or other techniques like folding) over the Option returned.
trait SomeAPI {
def fetch(): Future[Option[String]]
}
object APIImpl {
def asyncDBCall(): Future[List[String]] = ???
override def fetch(): Future[Option[String]] = asyncDBCall().map(r => if (r.isEmpty) None else Some(r.head)
}
However, Futures already give us a way to say that there are no results making it fail. But I don't know if this is a proper or expressive way of say that the call doesn't return results.
trait SomeAPI {
def fetch(): Future[String]
}
class NoResultsException extends Exception
object APIImpl extends SomeAPI {
def asyncDBCall(): Future[List[String]] = ???
override def fetch(): Future[String] = asyncDBCall().map(r => if (r.isEmpty) throw new NoResultsException() else r.head)
}
At first it seems that both options are acceptable and the final decision may be just about personal preference, but maybe I'm missing something and make a Future fail is not a good option.
Exceptions should be reserved for errors. If the API may or may not returns results, and both cases are considered normal, then the API should use Option.
If that makes it more verbose by forcing the client to handle both situations, that's good, because the client should handle both situations. Exceptions are good when you don't want to force the client code to handle an exceptional case (unexpected error) which should probably cascade and be handled by an error handler.
Now, if lack of results truly indicates that something went wrong, then an exception would be appropriate.
I'd expect clients want to treat "success, no results" and failure differently, generally speaking (e.g. retry in the second case, but not the first). If you denote the first with a failed future, you make verbosity for the clients worse, not better: they just need to check the exception instead of the success, and the API isn't helping them to be aware of that like Future[Option[A]] would.
If you really worry about verbosity, you could add custom extractors or methods for the clients to use if they want, e.g.
implicit class FutureOption[A](future: Future[Option[A]]) {
def mapOpt[B](handleSuccessWithResult: A => B, handleSuccessWithNoResult: () => B) = Future.map {
case Some(x) => onSuccessWithResult(x)
case None => onSuccessWithNoResult()
}
}
The others are right. Failures are not a good way to describe the lack of a concrete result, if this is part of regular operation.
Scala gives you another possibility to explicitly note, that there may be no results, sealed traits.
sealed trait MyOperationReturn
case object OkNoResult
case class OkWithStringResponse(data: String) extends MyOperationReturn
case class OkWithIntResponse(data: Int) extends MyOperationReturn
The user can then just pattern match. This option is especially handy, if you have either multiple return types (not only two) and no natural empty return type (like Seq.empty)

What is the advantage of using Option.map over Option.isEmpty and Option.get?

I am a new to Scala coming from Java background, currently confused about the best practice considering Option[T].
I feel like using Option.map is just more functional and beautiful, but this is not a good argument to convince other people. Sometimes, isEmpty check feels more straight forward thus more readable. Is there any objective advantages, or is it just personal preference?
Example:
Variation 1:
someOption.map{ value =>
{
//some lines of code
}
} orElse(foo)
Variation 2:
if(someOption.isEmpty){
foo
} else{
val value = someOption.get
//some lines of code
}
I intentionally excluded the options to use fold or pattern matching. I am simply not pleased by the idea of treating Option as a collection right now, and using pattern matching for a simple isEmpty check is an abuse of pattern matching IMHO. But no matter why I dislike these options, I want to keep the scope of this question to be the above two variations as named in the title.
Is there any objective advantages, or is it just personal preference?
I think there's a thin line between objective advantages and personal preference. You cannot make one believe there is an absolute truth to either one.
The biggest advantage one gains from using the monadic nature of Scala constructs is composition. The ability to chain operations together without having to "worry" about the internal value is powerful, not only with Option[T], but also working with Future[T], Try[T], Either[A, B] and going back and forth between them (also see Monad Transformers).
Let's try and see how using predefined methods on Option[T] can help with control flow. For example, consider a case where you have an Option[Int] which you want to multiply only if it's greater than a value, otherwise return -1. In the imperative approach, we get:
val option: Option[Int] = generateOptionValue
var res: Int = if (option.isDefined) {
val value = option.get
if (value > 40) value * 2 else -1
} else -1
Using collections style method on Option, an equivalent would look like:
val result: Int = option
.filter(_ > 40)
.map(_ * 2)
.getOrElse(-1)
Let's now consider a case for composition. Let's say we have an operation which might throw an exception. Additionaly, this operation may or may not yield a value. If it returns a value, we want to query a database with that value, otherwise, return an empty string.
A look at the imperative approach with a try-catch block:
var result: String = _
try {
val maybeResult = dangerousMethod()
if (maybeResult.isDefined) {
result = queryDatabase(maybeResult.get)
} else result = ""
}
catch {
case NonFatal(e) => result = ""
}
Now let's consider using scala.util.Try along with an Option[String] and composing both together:
val result: String = Try(dangerousMethod())
.toOption
.flatten
.map(queryDatabase)
.getOrElse("")
I think this eventually boils down to which one can help you create clear control flow of your operations. Getting used to working with Option[T].map rather than Option[T].get will make your code safer.
To wrap up, I don't believe there's a single truth. I do believe that composition can lead to beautiful, readable, side effect deferring safe code and I'm all for it. I think the best way to show other people what you feel is by giving them examples as we just saw, and letting them feel for themselves the power they can leverage with these sets of tools.
using pattern matching for a simple isEmpty check is an abuse of pattern matching IMHO
If you do just want an isEmpty check, isEmpty/isDefined is perfectly fine. But in your case you also want to get the value. And using pattern matching for this is not abuse; it's precisely the basic use-case. Using get allows to very easily make errors like forgetting to check isDefined or making the wrong check:
if(someOption.isEmpty){
val value = someOption.get
//some lines of code
} else{
//some other lines
}
Hopefully testing would catch it, but there's no reason to settle for "hopefully".
Combinators (map and friends) are better than get for the same reason pattern matching is: they don't allow you to make this kind of mistake. Choosing between pattern matching and combinators is a different question. Generally combinators are preferred because they are more composable (as Yuval's answer explains). If you want to do something covered by a single combinator, I'd generally choose them; if you need a combination like map ... getOrElse, or a fold with multi-line branches, it depends on the specific case.
It seems similar to you in case of Option but just consider the case of Future. You will not be able to interact with the future's value after going out of Future monad.
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Promise
import scala.util.{Success, Try}
// create a promise which we will complete after sometime
val promise = Promise[String]();
// Now lets consider the future contained in this promise
val future = promise.future;
val byGet = if (!future.value.isEmpty) {
val valTry = future.value.get
valTry match {
case Success(v) => v + " :: Added"
case _ => "DEFAULT :: Added"
}
} else "DEFAULT :: Added"
val byMap = future.map(s => s + " :: Added")
// promise was completed now
promise.complete(Try("PROMISE"))
//Now lets print both values
println(byGet)
// DEFAULT :: Added
println(byMap)
// Success(PROMISE :: Added)

Scala: How to return a Some or Option

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.