How to return upon encountering first "true" in a List[IO[Boolean]] in Scala Cats Effect - scala

Say I have a set of rules that have a validation function that returns IO[Boolean] at runtime.
case class Rule1() {
def validate(): IO[Boolean] = IO.pure(false)
}
case class Rule2() {
def validate(): IO[Boolean] = IO.pure(false)
}
case class Rule3() {
def validate(): IO[Boolean] = IO.pure(true)
}
val rules = List(Rule1(), Rule2(), Rule3())
Now I have to iterate through these rules and see "if any of these rules" hold valid and if not then throw exception!
for {
i <- rules.map(_.validate()).sequence
_ <- if (i.contains(true)) IO.unit else IO.raiseError(new RuntimeException("Failed"))
} yield ()
The problem with the code snippet above is that it is trying to evaluate all the rules! What I really want is to exit at the encounter of the first true validation.
Not sure how to achieve this using cats effects in Scala.

I claim that existsM is the most direct way to achieve what you want. It behaves pretty much the same as exists, but for monadic predicates:
for {
t <- rules.existsM(_.validate())
_ <- IO.raiseUnless(t)(new RuntimeException("Failed"))
} yield ()
It also stops the search as soon as it finds the first true.
The raiseUnless is just some syntactic sugar that's equivalent to the if-else from your question.

If you take a look at list of available extension methods in your IDE, you can find findM:
for {
opt <- rules.findM(_.validate())
_ <- opt match {
case Some(_) => IO.unit
case None => IO.raiseError(new RuntimeException("Failed")
}
} yield ()
Doing it manually could be done with foldLeft and flatMap:
rules.foldLeft(IO.pure(false)) { (valueSoFar, nextValue) =>
valueSoFar.flatMap {
case true => IO.pure(true) // can skip evaluating nextValue
case false => nextValue.validate() // need to find the first true IO yet
}
}.flatMap {
case true => IO.unit
case false => IO.raiseError(new RuntimeException("Failed")
}
The former should have the additional advantage that it doesn't have to iterate over whole collection when it finds the first match, while the latter will still go through all items, even if will start discarding them at some point. findM solves that by using tailRecM internally to terminate the iteration on first met condition.

You can try recursive
def firstTrue(rules: List[{def validate(): IO[Boolean]}]): IO[Unit] = rules match {
case r :: rs => for {
b <- r.validate()
res <- if (b) IO.unit else firstTrue(rs)
} yield res
case _ => IO.raiseError(new RuntimeException("Failed"))
}

Another approach is not using booleans at all, but the monad capabilities of IO
def validateRules(rules: List[Rule]): IO[Unit] =
rules.traverse_ { rule =>
rule.validate().flatMap { flag =>
IO.raiseUnless(flag)(new RuntimeException("Failed"))
}
}

Related

Execute operation on Future's value for its side effects, discarding result and retaining original value, but retaining order of operation

Say I have the following operations that must proceed in order:
Get blog post
Post analytics
Forward blog post
In code it may look like this:
val blogPostFut: Future[BlogPost] = blogService.getPost(postId)
val afterAnalytics: Future[BlogPost] = blogPostFut.flatMap(blogPost =>
val ignoredResponse: Future[Analytics] = analyticsService.sendAnalytics(blogPost)
ignoredResponse.map(_ => blogPost) // <-- THIS BOTHERS ME
)
val finalValue: Future[ForwardResult] = afterAnalytics.flatMap(blogPost =>
forwardService.forward(blogPost)
)
I am bothered that, in order to ensure proper ordering of execution, I have to pass forward blogPost within ignoredResponse in order to ensure it is available for step 3.
I'd love if I could do something like this:
blogPostFut.magicalFlatMap(analyticsService.sendAnalytics)
Where magicalFlatMap might be implemented like so:
// pseudocode
def magicalFlatMap[A,B](f: A => Future[B]): Future[A] = f().map(_ => this.value)
Does magicalFlatMap exist in either the Scala stdlib or in Cats? Is it possible to map a Future for side effects while automatically retaining the value of the original Future and strict ordering of operations?
magicalFlatMap seems to be cats.FlatMap#flatTap
https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/FlatMap.scala#L150
Try Future.andThen for side-effects
for {
blogPost <- blogService.getPost(postId).andThen { case Success(post) => analyticsService.sendAnalytics(post) }
finalValue <- forwardService.forward(blogPost)
} yield {
finalValue
}
Here is a dummy example
val result = for {
v1 <- Future(1)
v2 <- Future(v1 + 2).andThen { case Success(v) => println(v) }
v3 <- Future(v1 + v2)
} yield {
v3
}
result.foreach(println)
which should output
3
4
We could also do
for {
blogPost <- blogService.getPost(postId)
_ <- analyticsService.sendAnalytics(blogPost)
finalValue <- forwardService.forward(blogPost)
} yield {
finalValue
}
however in this case failure in analyticsService.sendAnalytics(blogPost) would short-circuit the whole for-comprehension which might not be desirable.

Scala for/yield with multiple optional calls based on credentials

I need to get an entire case class, built with 3 other case classes, back in one call. However, depending on a users credentials they may or may not have access to certain parts of that case class.
Below is how I'm currently doing it.(and it works) It's making the call but clearing out the unauthorized case classes afterwards. I would ideally like to stop the call (due to low bandwidth) if they don't have the proper credentials and return None.
The persistent entity service call is expecting an Option[PersonOne] and Option[PersonTwo] so the .ask must return that or it says it's returning an object and won't compile.
override def getPerson(id: UUID, credOne: Option[Boolean], credTwo: Option[Boolean]) = ServerServiceCall { _ =>
for {
g <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonGeneral)
h <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonOne) //if (credOne) **this 'if' does not work**
s <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonTwo)
} yield {
val x: Option[PersonOne] = if (credOne == Some(true)) h else None
val y: Option[PersonTwo] = if (credTwo == Some(true)) s else None
CompletePerson(g.get, x, y)
//TODO catch if no employee is returned
//throw NotFound (s"Person not Found with $id");
}
}
Here is a version of your function that will only call ask if the appropriate credential value is Some(true):
override def getPerson(id: UUID, credOne: Option[Boolean], credTwo: Option[Boolean]) = ServerServiceCall { _ =>
CompletePerson(
registry.refFor[PersonEntity](id.toString()).ask(GetPersonGeneral).get,
credOne.filter(_ == true).flatMap(_ => registry.refFor[PersonEntity](id.toString()).ask(GetPersonOne)),
credTwo.filter(_ == true).flatMap(_ => registry.refFor[PersonEntity](id.toString()).ask(GetPersonTwo))
)
}
The key part is this:
credX.filter(_ == true).flatMap(...)
If credX is None this will return None
If credX contains false it will return None
If credX is Some(true) then flatMap will call the ask function
If ask returns None then the expression will return None, otherwise it will return Some[PersonX]
I am a bit unclear on some of the data types, but I think this should give you some idea of how to approach this code. (For example, that bare .get look dangerous as it could throw an exception)
Edit after comments
I fixed an issue with the filter(_), it should be filter(_ == true).
It looks like ask actually returns Option[Option[T]], in which case this might be closer to what is needed.
def getPerson(id: UUID, credOne: Option[Boolean], credTwo: Option[Boolean]) = ServerServiceCall { _ =>
for {
g <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonGeneral)
gen <- g
} yield {
val p1 = credOne.filter(_ == true).flatMap(_ => registry.refFor[PersonEntity](id.toString()).ask(GetPersonOne))).flatten
val p2 = credTwo.filter(_ == true).flatMap(_ => registry.refFor[PersonEntity](id.toString()).ask(GetPersonTwo))).flatten
CompletePerson(gen, p1, p2)
}
}
I'm not quite sure what you're asking, but are you concerned that if
registry.refFor[PersonEntity](id.toString()).ask(GetPersonGeneral)
returns None that you'll keep making expensive .ask calls? Cos that won't happen, as we can see from this
def giveMeSome = {println("giving you Some"); Some(1)}
def giveMeNone = {println("giving you None"); None}
def giveMeSomeMore = {println("giving you Some more"); Some(2)}
val ans = for {
a <- giveMeSome
f <- giveMeNone
q <- giveMeSomeMore
} yield (a, f, q)
println(ans)
which prints
giving you Some
giving you None
None
We can also see that in this case, you'll get None back. I can't tell if you want a None or an error thrown. If you want an error, you can just wrap what's above
I didn't get your question completely, but based on my understanding you want to first check if credOne == Some(true) && credTwo == Some(true) then proceed further else return None
override def getPerson(id: UUID, credOne: Option[Boolean], credTwo: Option[Boolean])
=
for {
cone <- credOne
ctwo <- credTwo
if cone
if ctwo
} yield {
ServerServiceCall { _ =>
for {
g <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonGeneral)
h <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonOne)
s <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonTwo)
if g.isDefined
} yield {
CompletePerson(g.get, h, s)
}
}

Scala's for comprehension for Futures and Options

I have recently read Manuel Bernhardt's new book Reactive Web Applications. In his book, he states that Scala developers should never use .get to retrieve an optional value.
I want to pick up his suggestions but I am struggling to avoid .get when using for comprehensions for Futures.
Let's say I have the following code:
for {
avatarUrl <- avatarService.retrieve(email)
user <- accountService.save(Account(profiles = List(profile.copy(avatarUrl = avatarUrl)))
userId <- user.id
_ <- accountTokenService.save(AccountToken.create(userId, email))
} yield {
Logger.info("Foo bar")
}
Normally, I would have used AccountToken.create(user.id.get, email) instead of AccountToken.create(userId, email). However, when trying to avoid this bad practice, I get the following exception:
[error] found : Option[Nothing]
[error] required: scala.concurrent.Future[?]
[error] userId <- user.id
[error] ^
How can I solve this?
First option
If you really want to use for comprehension you'll have to separate it to several fors, where each works with the same monad type:
for {
avatarUrl <- avatarService.retrieve(email)
user <- accountService.save(Account(profiles = List(profile.copy(avatarUrl = avatarUrl)))
} yield for {
userId <- user.id
} yield for {
_ <- accountTokenService.save(AccountToken.create(userId, email))
}
Second option
Another option is to avoid Future[Option[T]] altogether and use Future[T] which can materialize into Failure(e) where e is a NoSuchElementException whenever you expect a None (in your case, the accountService.save() method):
def saveWithoutOption(account: Account): Future[User] = {
this.save(account) map { userOpt =>
userOpt.getOrElse(throw new NoSuchElementException)
}
}
Then you'll have:
(for {
avatarUrl <- avatarService.retrieve(email)
user <- accountService.saveWithoutOption(Account(profiles = List(profile.copy(avatarUrl = avatarUrl)))
_ <- accountTokenService.save(AccountToken.create(user.id, email))
} yield {
Logger.info("Foo bar")
}) recover {
case t: NoSuchElementException => Logger.error("boo")
}
Third option
Fall back to map/flatMap and introduce intermediate results.
Let's take a step back and explore the meaning of our expression:
A Future is "eventually a value (but might fail)"
An Option is "maybe a value"
What are the semantics of Future[Option]? Let's explore the values to gain some intuition:
Future[Option]
Success(Some(x)) => Good. Let's do stuff with x
Success(None) => Finished but got nothing => This is probably an application-level error
Failure(_) => Something went wrong, so we don't have a value
We can flatten Success(None) into a Failure(SomeApplicationException) and eliminate the need of handling the Option separately.
For that, we can define a generic function to turn an Option into a Future and use the for-comprehension to apply the flattening.
def optionToFuture[T](opt:Option[T], ex: ()=>Exception):Future[T] = opt match {
case Some(v) => Future.successful(v)
case None => Future.failed(ex())
}
We can now express our computation fluently with a for-comprehension:
for {
avatarUrl <- avatarService.retrieve(email)
user <- accountService.save(Account(profiles = List(profile.copy(avatarUrl = avatarUrl)))
userId <- optionToFuture(user.id, () => new UserNotFoundException(email))
_ <- accountTokenService.save(AccountToken.create(userId, email))
} yield {
Logger.info("Foo bar")
}
Stop Option propogation by failing the Future when option is None
Fail the future when id is none and abort
for {
....
accountOpt <-
user.id.map { id =>
Account.create(id, ...)
}.getOrElse {
Future.failed(new Exception("could not create account."))
}
...
} yield result
Better to have a custom exception like
case class NoIdException(msg: String) extends Exception(msg)
invoking .get on Option should be done only if you are sure that option is Some(x) otherwise .get will throw an exception.
Thats by using .get is not good practise because it may cause an exception in the code.
Instead of .get its good practice to use getOrElse.
You can map or flatMap the option to get access to the inner value.
Good practice
val x: Option[Int] = giveMeOption()
x.getOrElse(defaultValue)
Get can be used here
val x: Option[Int] = giveMeOption()
x.OrElse(Some(1)).get

How to improve the code of a method which uses 'Free monad'?

I'm trying some code which inspects this slides about Free Monad in Scala, and made a small project with some slightly changed code.
The project is here: https://github.com/freewind/free-the-monads
Everything seems good at first, the code is clean and beautiful:
def insertAndGet() = for {
_ <- Script.insert("name", "Freewind")
value <- Script.get("name")
} yield value
def insertAndDelete() = for {
_ <- Script.insert("name", "Freewind")
_ <- Script.delete("name")
value <- Script.get("name")
} yield value
def insertAndUpdateAndDelete() = for {
_ <- Script.insert("name", "Freewind1")
oriValue <- Script.update("name", "Freewind2")
_ <- Script.delete("name")
finalValue <- Script.get("name")
} yield (oriValue, finalValue)
But when my logic is complex, e.g. there is some Script[Option[_]], and I need to check the option value to decide to do something, I can't use the for-comprehension any more, the code is like:
private def isLongName(name: String): Script[Boolean] = for {
size <- Script.getLongNameConfig
} yield size.exists(name.length > _)
def upcaseLongName(key: String): Script[Option[String]] = {
Script.get(key) flatMap {
case Some(n) => for {
isLong <- isLongName(n)
} yield isLong match {
case true => Some(n.toUpperCase)
case false => Some(n)
}
case _ => Script.pure(None)
}
}
I found the Free Monad approach is really interesting and cool, but I'm not familiar with scalaz, and just beginning to learn Monad things, not sure how to improve it.
Is there any way to make it better?
PS: You can just clone the project https://github.com/freewind/free-the-monads and try it yourself
This is a good use case for the OptionT monad transformer:
import scalaz.OptionT, scalaz.syntax.monad._
def upcaseLongName(key: String): OptionT[Script, String] = for {
n <- OptionT.optionT(Script.get(key))
isLong <- isLongName(n).liftM[OptionT]
} yield if (isLong) n.toUpperCase else n
Here OptionT.optionT converts a Script[Option[String]] into an OptionT[Script, String], and .liftM[OptionT] raises a Script[Boolean] into the same monad.
Now instead of this:
println(upcaseLongName("name1").runWith(interpreter))
You'd write this:
println(upcaseLongName("name1").run.runWith(interpreter))
You could also have upcaseLongName return an Script[Option[String]] directly by calling run there, but if there's any chance you'll need to be composing it with other option-y script-y things it's probably best to have it return OptionT[Script, String].

Cleanest way in Scala to avoid nested ifs when transforming collections and checking for error conditions in each step

I have some code for validating ip addresses that looks like the following:
sealed abstract class Result
case object Valid extends Result
case class Malformatted(val invalid: Iterable[IpConfig]) extends Result
case class Duplicates(val dups: Iterable[Inet4Address]) extends Result
case class Unavailable(val taken: Iterable[Inet4Address]) extends Result
def result(ipConfigs: Iterable[IpConfig]): Result = {
val invalidIpConfigs: Iterable[IpConfig] =
ipConfigs.filterNot(ipConfig => {
(isValidIpv4(ipConfig.address)
&& isValidIpv4(ipConfig.gateway))
})
if (!invalidIpConfigs.isEmpty) {
Malformatted(invalidIpConfigs)
} else {
val ipv4it: Iterable[Inet4Address] = ipConfigs.map { ipConfig =>
InetAddress.getByName(ipConfig.address).asInstanceOf[Inet4Address]
}
val dups = ipv4it.groupBy(identity).filter(_._2.size != 1).keys
if (!dups.isEmpty) {
Duplicates(dups)
} else {
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
val taken: Iterable[Inet4Address] = ipAvailability.filter(!_._2).keys
if (!taken.isEmpty) {
Unavailable(taken)
} else {
Valid
}
}
}
}
I don't like the nested ifs because it makes the code less readable. Is there a nice way to linearize this code? In java, I might use return statements, but this is discouraged in scala.
I personally advocate using a match everywhere you can, as it in my opinion usually makes code very readable
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil =>
val ipv4it = ipConfigs.map { ipc =>
InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address]
}
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil =>
val taken = ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys
if (taken.nonEmpty) Unavailable(taken) else Valid
case dups => Duplicates(dups)
}
case invalid => Malformatted(invalid)
}
Note that I've chosen to match on the else part first, since you generally go from specific to generic in matches, since Nil is a subclass of Iterable I put that as the first case, eliminating the need for an i if i.nonEmpty in the other case, since it would be a given if it didn't match Nil
Also a thing to note here, all your vals don't need the type explicitly defined, it significantly declutters the code if you write something like
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
as simply
val ipAvailability = ipv4it.map(ip => (ip, isIpAvailable(ip)))
I've also taken the liberty of removing many one-off variables I didn't find remotely necessary, as all they did was add more lines to the code
A thing to note here about using match over nested ifs, is that is that it's easier to add a new case than it is to add a new else if 99% of the time, thereby making it more modular, and modularity is always a good thing.
Alternatively, as suggested by Nathaniel Ford, you can break it up into several smaller methods, in which case the above code would look like so:
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil => wellFormatted(ipConfigs)
case i => Malformatted(i)
}
def wellFormatted(ipConfigs: Iterable[IpConfig]): Result = {
val ipv4it = ipConfigs.map(ipc => InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address])
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil => noDuplicates(ipv4it)
case dups => Duplicates(dups)
}
}
def noDuplicates(ipv4it: Iterable[IpConfig]): Result =
ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys match {
case Nil => Valid
case taken => Unavailable(taken)
}
This has the benefit of splitting it up into smaller more manageable chunks, while keeping to the FP ideal of having functions that only do one thing, but do that one thing well, rather than having god-methods that do everything.
Which style you prefer, of course is up to you.
This has some time now but I will add my 2 cents. The proper way to handle this is with Either. You can create a method like:
def checkErrors[T](errorList: Iterable[T], onError: Result) : Either[Result, Unit] = if(errorList.isEmpty) Right() else Left(onError)
so you can use for comprehension syntax
val invalidIpConfigs = getFormatErrors(ipConfigs)
val result = for {
_ <- checkErrors(invalidIpConfigs, Malformatted(invalidIpConfigs))
dups = getDuplicates(ipConfigs)
_ <- checkErrors(dups, Duplicates(dups))
taken = getAvailability(ipConfigs)
_ <- checkErrors(taken, Unavailable(taken))
} yield Valid
If you don't want to return an Either use
result.fold(l => l, r => r)
In case of the check methods uses Futures (could be the case for getAvailability, for example), you can use cats library to be able of use it in a clean way: https://typelevel.org/cats/datatypes/eithert.html
I think it's pretty readable and I wouldn't try to improve it from there, except that !isEmpty equals to nonEmpty.