How do I execute a list of effects? - scala

I have a value of this type:
val effects : List[EitherT[Future, Error, Foo]] = ...
How do I run through the list and execute those effects? I did
effects.traverse
But that method is not defined for that type.

As mentioned in comments, it's better to get rid of Future when you are declaring some list of effects and want to execute them and gather effects in one list after. It's better to use IO or another lazy evaluated effect.
So, if you just need to traverse on Future[List[A]]:
import cats.data.EitherT
import scala.concurrent.{ExecutionContext, Future}
import cats.syntax.traverse._
import cats.instances.list._
import cats.instances.future._
implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.global
case class Foo()
val effects : List[EitherT[Future, Error, Foo]] = ???
def f(e: EitherT[Future, Error, Foo]): Future[Either[Error, Foo]] = ???
val result: Future[List[Either[Error, Foo]]] = effects.traverse(f)
this code compiles and all you need is implement f. You need to add import cats.instances.future._ to have traverse function in Furure.
but better way to construct you process on something like IO:
import cats.data.EitherT
import cats.syntax.traverse._
import cats.instances.list._
import cats.effect.IO
val effects : List[EitherT[IO, Error, Foo]] = ???
def f(e: EitherT[IO, Error, Foo]): IO[Either[Error, Foo]] = ???
val result: IO[List[Either[Error, Foo]]] = effects.traverse(f)
and call it using unsafeRunAsync.

Related

Shorthand for map traverse reduce

I'm looking for a short way to traverse and reduce at the same time. Here is the solution I came up with:
import cats.{Applicative, Monad}
import cats.instances.list._
import cats.syntax.functor._
import cats.syntax.flatMap._
import cats.syntax.traverse._
object Test extends App {
def intFM[F[_], M[_]](i: Int): F[M[Int]] = ???
def traverseReduce[F[_]: Applicative, M[_]: Monad](lst: List[Int]) =
lst.traverse(intFM[F, M]).map(_.reduce(_ >> _))
}
As can be seen I did 3 operations: traverse, map and reduce, but I expected that it was possible to do the reduction while traversing. Is there some shorthand?
You can try without traversing
def traverseReduce[F[_]: Applicative, M[_]: Monad](lst: List[Int]): F[M[Int]] =
lst.map(intFM[F, M]).reduce((_, _).mapN(_ >> _))

cats.data.EitherT with traverse

I have a question about using Traverse together with EitherT. Let's say we have a code like this:
def validate(s: String): EitherT[Future, NumberFormatException, Int] = {
EitherT(Future.successful(try { Right(s.toInt) } catch { case e:
NumberFormatException => Left(e)}))
}
List("1", "2").traverse(validate)
Unfortunately this code does not compile, because we are missing something:
error: could not find implicit value for evidence parameter of type cats.Applicative[G] List("1", "2").traverse(validate)
I tried to look this up and found for example this answer: Switching between EitherT and Validation to accumulate error or traverse or Validation versus disjunction
So it seems some solution could exists. But the problem is that both of them are using traverseU, which is no longer an option in scala 2.12. So how can this be done please?
EDIT
This is the code including imports:
import cats.data.EitherT
import cats.syntax.traverse._
import cats.instances.list._
import cats.instances.future._
import scala.concurrent.ExecutionContext.global
import scala.concurrent.Future
def validate(s: String): EitherT[Future, NumberFormatException, Int] = {
EitherT(Future.successful(try { Right(s.toInt) } catch { case e:
NumberFormatException => Left(e)}))
}
List("1", "2").traverse(validate)
Usually cats will pack specific evidence implicits under different imports. In this case, you need the proofs for List and Future.
import cats.data.EitherT
import cats.syntax.traverse._
import cats.instances.list._
import cats.instances.future._
import scala.concurrent.Future
def validate(s: String): EitherT[Future, NumberFormatException, Int] = {
EitherT(Future.successful(try { Right(s.toInt) } catch { case e: NumberFormatException => Left(e) }))
}
List("1", "2").traverse(validate)
This code compiles with cats 1.2.0 for me.
import cats.{Applicative, Monad}
import cats.data.EitherT
import cats.syntax.traverse._
import cats.instances.list._
import cats.instances.future._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
def validate(s: String): EitherT[Future, NumberFormatException, Int] = {
EitherT(Future.successful(try { Right(s.toInt) } catch { case e: NumberFormatException => Left(e) }))
}
type Tmp[T] = EitherT[Future, NumberFormatException,T]
List("1", "2").traverse[Tmp, Int](validate)
So this is how it works to me. I had to create new type.

Error while trying to pass Byte through TCP connection

I need to pass byte like "0x02" through tcp connection and process it on a server. Code of client is here:
package protocol
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Source, Tcp}
import akka.util.ByteString
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Source, Tcp}
import akka.util.ByteString
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
class UpperServiceClient(ip: String, port: Int) {
def run = {
implicit val system = ActorSystem("ClientSys")
implicit val materializer = ActorMaterializer()
val a1 = Array("0x02", "0x02").toSeq
val testInput = a1.map(ByteString(_))
val result: Future[ByteString] = Source(testInput).via(Tcp().outgoingConnection(ip, port)).
runFold(ByteString.empty) { (acc, in) => acc ++ in }
val res: ByteString = Await.result(result, 10.seconds)
}
}
But IDEA shows me error:
Type mismatch, expected: Iterable[NotInferedT], actual: Seq[ByteString]
What should I do to pass "0x02" as whole byte?
The Source factory method that you're using expects an immutable Iterable. By default, calling .toSeq on an array returns a mutable Seq. One way to address this is to call .toList instead:
val a1 = Array("0x02", "0x02").toList

How do I supply an implicit value for an akka.stream.Materializer when sending a FakeRequest?

I'm trying to make sense of the error(s) I'm seeing below, and to learn how to fix it.
could not find implicit value for parameter materializer: akka.Stream.Materializer
val fut: Future[Result] = action.apply(fakeRequest).run
^
not enough arguments for method run (implicit materializer: akka.stream.Materializer)scala.concurrent.Future[play.api.mvc.Result].
Unspecified value parameter materializer.
val fut: Future[Result] = action.apply(fakeRequest).run
^
Here is the test code that produced the error(s):
package com.foo.test
import com.foo.{Api, BoundingBox}
import org.scalatest.{FlatSpec, Matchers}
import play.api.libs.json._
import play.api.mvc._
import play.api.test.{FakeHeaders, FakeRequest}
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
class TestJmlPlay extends FlatSpec with Matchers {
val bbox = new BoundingBox(-76.778154438007732F, 39.239828198015971F, -76.501003519894326F, 39.354663763993926F)
"latitudes" should "be between swLat and neLat" in {
val action: Action[AnyContent] = (new Api).getForPlay(bbox)
val jsonStr = getStringFromAction(action)
areLatitudesOk(jsonStr, bbox) shouldBe true
}
private def getStringFromAction(action:Action[AnyContent]):String = {
val fakeRequest: Request[String] = new FakeRequest("fakeMethod", "fakeUrl", new FakeHeaders, "fakeBody")
val fut: Future[Result] = action.apply(fakeRequest).run // <== ERROR!
val result = Await.result(fut, 5000 milliseconds)
result.body.toString
}
private def areLatitudesOk(jsonStr: String, bbox: BoundingBox): Boolean = ...
}
You can create an implicit ActorMaterializer within your test class which will use testkit's ActorSystem:
import akka.testkit.TestKit
import akka.actor.ActorSystem
class TestJmlPlay(_system : ActorSystem) extends TestKit(_system) ... {
implicit val materializer: ActorMaterializer = ActorMaterializer()
val bbox = ...
You don't need Materializer.
I believe you are calling not the right action.apply method.
You want def apply(request: Request[A]): Future[Result]
To call the right, you need FakeRequest[AnyContent], same parametrized type as action:Action[AnyContent].This type is forced by PlayBodyParser I believe you set for your action.
After that you don't need .run call

Not enough arguments for method unmarshal: (implicit evidence$1: spray.httpx.unmarshalling.FromResponseUnmarshaller

I am passing from SprayJsonSupport to argonaut based on this example.
After some code modification :
object ElevationJsonProtocol extends DefaultJsonProtocol {
implicit val locationCodec: CodecJson[Elevation] = casecodec2(Elevation, Elevation.unapply)("location", "elevation")
implicit val elevationCodec: CodecJson[Location] = casecodec2(Location, Location.unapply)("lat", "lng")
implicit def googleApiResultCodec: CodecJson[GoogleApiResult] = casecodec2(GoogleApiResult, GoogleApiResult.unapply)("status", "results")
}
I got this error
Error:(41, 42) not enough arguments for method unmarshal: (implicit evidence$1: spray.httpx.unmarshalling.FromResponseUnmarshaller[GoogleApiResult])spray.http.HttpResponse => GoogleApiResult.
Unspecified value parameter evidence$1.
val pipeline = sendReceive ~> unmarshal[GoogleApiResult]
^
I take a look at the unmarshall method:
def unmarshal[T](implicit evidence$1 : spray.httpx.unmarshalling.FromResponseUnmarshaller[T]) : scala.Function1[spray.http.HttpResponse, T]
How can I add the implicit parameter? and why I did not got such error whith the sprayJsonSupport ?
The hole code :
import spray.httpx.unmarshalling.FromResponseUnmarshaller
import scala.util.{Success, Failure}
import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.event.Logging
import akka.io.IO
import spray.json.{JsonFormat, DefaultJsonProtocol}
import spray.can.Http
import spray.httpx.SprayJsonSupport
import spray.client.pipelining._
import spray.util._
import argonaut._, Argonaut._
case class Elevation(location: Location, elevation: Double)
case class Location(lat: Double, lng: Double)
case class GoogleApiResult(status: String, results: List[Elevation])
object ElevationJsonProtocol extends DefaultJsonProtocol {
implicit val locationCodec: CodecJson[Elevation] = casecodec2(Elevation, Elevation.unapply)("location", "elevation")
implicit val elevationCodec: CodecJson[Location] = casecodec2(Location, Location.unapply)("lat", "lng")
implicit def googleApiResultCodec: CodecJson[GoogleApiResult] = casecodec2(GoogleApiResult, GoogleApiResult.unapply)("status", "results")
}
object Main extends App {
// we need an ActorSystem to host our application in
implicit val system = ActorSystem("simple-spray-client")
import system.dispatcher // execution context for futures below
val log = Logging(system, getClass)
log.info("Requesting the elevation of Mt. Everest from Googles Elevation API...")
import ElevationJsonProtocol._
val pipeline = sendReceive ~> unmarshal[GoogleApiResult]
val responseFuture = pipeline (
Get("http://maps.googleapis.com/maps/api/elevation/json?locations=27.988056,86.925278&sensor=false")
)
responseFuture onComplete {
case Success(GoogleApiResult(_, Elevation(_, elevation) :: _)) =>
log.info("The elevation of Mt. Everest is: {} m", elevation)
shutdown()
case Success(somethingUnexpected) =>
log.warning("The Google API call was successful but returned something unexpected: '{}'.", somethingUnexpected)
shutdown()
case Failure(error) =>
log.error(error, "Couldn't get elevation")
shutdown()
}
def shutdown(): Unit = {
IO(Http).ask(Http.CloseAll)(1.second).await
system.shutdown()
}
}
I don't really use argonaut, I use play json with spray. But at a glance it seems like there needs to be an argonaut support trait/import pulled in for your implicit codecs to convert to spray's unmarshaller (similar thing is required for play json).
https://github.com/dwhjames/argonaut-spray
this library seems to be what you want. Your implicits and imports look fine, pulling in the library should solve your problem.