How to write a Future method in foreach concisely - scala

I'm a beginner in Scala. Is there a concise way to write the following nested case statement?
def delete(
client : TwitterRestClient,
userId : Long,
keyword : String) : Unit = {
client.userTimelineForUserId(userId).onComplete{
case Success(value) => {
value.data.filter(_.text.contains(keyword)).foreach(x => {
client.deleteTweet(x.id).onComplete{
case Success(value) => println(s"Success: $value")
case Failure(exception) => println(s"Fail: $exception")
}
})
}
case Failure(exception) => println(s"Fail:$exception")
}

Slightly different way using flatMap, sequencing Futures and using collect instead of filter and map:
def delete(
client : TwitterRestClient,
userId : Long,
keyword : String) : Unit = {
client
.userTimelineForUserId(userId)
.flatMap(ratedData => Future.sequence(ratedData.data.collect {
case tweet if tweet.text.contains(keyword) => client.deleteTweet(tweet.id)
}))
.onComplete {
case Success(value) => println(s"Success: $value")
case Failure(exception) => println(s"Fail: $exception")
}
}
I have only compiled the code, dint run it.

Related

Scala Found Unit Expected Future[Result]

def update() = AuthAction.async(parse.json) { implicit request =>
val list = request.body.asInstanceOf[JsArray].value
list.foreach( mapping => {
repository.update()
}.andThen {
case Success(value) => repository.update2()
case Failure(exception) => {
BadRequest(errorResponse(Json.toJson(""), "updation failed"))
}
})
}
I have a controller function where I want to wait for a DB repo function ( repository.update2() )to complete and then send a response, but its saying "Found Unit expected Future[Result]"
Your success claus is presumably returning Unit where a Future response is required. Try this:
.andThen {
case Success(value) =>
repository.update2()
Future.successful(Ok("repository updated"))
case Failure(exception) =>
Future.successful(BadRequest(exception.getMessage())))
}

How to wrap effectful F instead of the concrete type constructor?

I have the following function definition:
private val checkSapHealth: IO[ServerHealth] =
BlazeClientBuilder[IO](global).resource.use { client =>
discovery
.senderAddr
.flatMap { addr =>
client
.get(addr.url |+| addr.health) { res =>
res.status match {
case Ok =>
IO(ServerOnline)
case _ =>
IO(ServerOffline)
}
}
.timeout(2.second)
.recover {
case _: Throwable => ServerOffline
}
}
}
I would like to replace the concrete type IO through F[_] to make it more abstract. The problem here is the line:
IO(ServerOnline)
The question is how to make it to
F(ServerOnline)
Try to use cats.effect.Sync
https://typelevel.org/cats-effect/typeclasses/sync.html
So basically using Sync[IO].delay is equivalent to using IO.apply.
private def checkSapHealth[F[_]: Sync]: F[ServerHealth] = ...
use ConcurrentEffect and add an implicit Applicative of F, thus giving you the option to lift values into the F context
private def checkSapHealth[F[_] : ConcurrentEffect](implicit A: Applicative[F]): F[ServerHealth] =
BlazeClientBuilder[F](global).resource.use { client =>
discovery
.senderAddr
.flatMap { addr =>
client
.get(addr.url |+| addr.health) { res =>
res.status match {
case Ok =>
A.pure(ServerOnline)
case _ =>
A.pure(ServerOffline)
}
}
.timeout(2.second)
.recover {
case _: Throwable => ServerOffline
}
}
}

MapTo Either silent fail when it's not matched

I have the following code that silently fail without catching any error:
(actor ? GetRowCount()).mapTo[Either[Rejection, Long]] map {
case Left(x) => ctx.reject(x)
case Right(totalRows) => ctx.complete(totalRows)
}
When GetRowCount() does not return a Long, but a String for instance, no error was caught and it just silently fail.
I am thinking to use the following:
(actor ? GetRowCount()).mapTo[Either[Rejection, Any]] map {
case Left(x) => ctx.reject(x)
case Right(totalRows: Long) => ctx.complete(totalRows)
case _ => ctx.reject(Rejection("Type mismatch"))
}
But is there a better answer?
I would use recover or recoverWith
(actor ? GetRowCount).mapTo[Either[Rejection, Long]] map {
case Left(x) => ctx.reject(x)
case Right(totalRows) => ctx.complete(totalRows)
} recover {
case e: Throwable =>
logger.error(e, "Some thing wrong while performing ask")
//throw an error or return something here
}

Generic Try[T] function

I want to refactor some common error handling logic in a generic Try[T] handler, similar to this:
def handler[T](t: Try[T], successFunc: T => Unit) = {
t.map {
case Success(res) => { // type mismatch required T, found Any (in successFunc line)
//case Success(res: T) => { // Type abstract type pattern T is unchecked since it is eliminated by erasure
successFunc(res)
}
case Failure(e: CustomException) => {
// custom actions
}
case Failure(e) => {
// custom actions
}
}
}
Seems I can't match against the type T because of type erasure. But I can't pass an Any to successFunc.
How can I implement this function?
Mapping on a try applies a function to the value held by a success of that try, what you have there is not a Success or a Failure, it's a T, what you want is a match:
def handler[T](t: Try[T], successFunc: T => Unit) = {
t match {
case Success(res) =>
successFunc(res)
case Failure(e: FileNotFoundException) =>
case Failure(e) =>
}
}
The usage in your case of map would be:
t.map(someT => successFunc(someT))

Elegant way of getting the Int value form Future[Option[Int]]

Is there a more elegant way of getting the Int value from Future[Option[Int]] instead of using finalFuture.value.get.get.get?
This is what I have so far:
val finalFuture: Future[Option[Int]] = result.contents
finalFuture.onComplete {
case Success(value) => println(s"Got the callback with value = ", finalFuture.value.get.get.get)
case Failure(e) => e.printStackTrace
}
You could nest the match:
finalFuture.onComplete {
case Success(Some(value)) => println(s"Got the callback with value = ", value)
case Success(None) => ()
case Failure(e) => e.printStackTrace
}
You can use foreach to apply a A => Unit function to the value in Option[A], if it exists.
fut.onComplete {
case Success(opt) => opt.foreach { val =>
println(s"Got the callback with value = {}", val)
}
case Falure(ex) => ex.printStackTrace
}
You can use also the toOption of Try to get Option[Option[Int]] and then flatten to get Option[Int]
def printVal(finalFuture: Future[Option[Int]] ) = finalFuture.onComplete(
_.toOption.flatten.foreach(x=> println (s"got {}",x))
)
EDIT: That assuming you don't care about the stacktrace :)