cats.data.EitherT with traverse - scala

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.

Related

How do I execute a list of effects?

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.

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

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.

How to use Spray JSON serializer in abstract way?

Any idea why this code doesn't work? I'm trying to have serialize method to all my exceptions to avoid the need for pattern matching for every exception. With the code below, I just need to case ex:MarshallableException => ex.serialize
The below code can run stand-alone. It will print Start... Serialization Failure shared.utils.InvalidID :(
import scala.util.{Failure, Success}
import akka.http.scaladsl.marshalling.{Marshaller, Marshal}
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model.ResponseEntity
import spray.json.DefaultJsonProtocol
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
trait MarshallableException{
def serialize: Future[ResponseEntity]
}
case class InvalidID(error:String, code: String= "6001") extends Exception with MarshallableException {
import Exceptions._
override def serialize: Future[ResponseEntity] = {
Marshal(this).to[ResponseEntity]
}
}
object Exceptions extends DefaultJsonProtocol{
implicit val invalidIDFormat = jsonFormat2(InvalidID.apply)
}
object Test extends App{
val ex = new InvalidID("Test exception")
println("Start...\n")
ex.serialize.onComplete{
case Success(e) => println(e.toString())
case Failure(f) =>
println("Serialization Failure " + f.toString)
}
}

Scalamock scalatest - Not able to stub

I have created a Workflow Processor Trait similar to the one mentioned below:
import org.scalatestplus.play._
import play.api.mvc._
import play.api.test._
import play.api.test.Helpers._
import org.scalatest.Matchers._
import org.scalamock.scalatest.MockFactory
import utils.OAuthUtils._
import utils.OAuthUtils
import utils.OAuthProcess
import play.api.Application
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import play.api.libs.json.JsResult
import play.api.libs.json.Json
import play.api.libs.json.JsSuccess
import play.api.libs.json.JsError
import play.api.libs.ws.WS
trait MyProcess[A] {
type B
type C
type D
def stage1(s: String): B
def stage2(b: B)(implicit e: ExecutionContext, app: Application, a: A): Future[C]
def stage3(c: C)(implicit e: ExecutionContext, app: Application, a: A): Future[JsResult[D]]
def process(s: String)(implicit e: ExecutionContext, app: Application, a: A): Future[JsResult[D]] = {
val b = stage1(s)
val f_d = for {
c <- stage2(b)
d <- stage3(c)
} yield d
f_d
}
}
Now I would like to Unit test this code. So I created a nicer little scalatest scalamock test suite:
class TestController extends PlaySpec with Results with MockFactory {
"MyController" must {
" testing for method process" in {
running(FakeApplication()) {
implicit val context = scala.concurrent.ExecutionContext.Implicits.global
implicit val s = "implicit String"
implicit val currentApp = play.api.Play.current
case class TestParms(s: String)
abstract class TestProcessor extends MyProcess[TestParms] {
type B = String
type C = String
type D = String
}
implicit val t = stub[TestProcessor]
t.stage1(_: String).when(token).returns(testToken)
(t.stage2(_: String)(_: ExecutionContext, _: Application, _: TestParms)).when(token, context, currentApp, tp).returns({
Future.successful(stage2String)
})
(t.stage3(_: String)(_: ExecutionContext, _: Application, _: TestParms)).when(token, context, currentApp, tp).returns({
Future.successful(Json.fromJson[String](Json.parse(stage3String)))
})
}
}
}
}
My expectation is to set this stub on a different class and test the class. Stubs (t.stage2 and t.stage3) compile fine but the following statement doesn't compile.
t.stage1(_: String).when(token).returns(testToken)
Compiler reports the following issue:
overloaded method value when with alternatives: (resultOfAfterWordApplication: org.scalatest.words.ResultOfAfterWordApplication)Unit <and> (f: => Unit)Unit cannot be applied to (String) TestController.scala /play-scala/test/controllers
Could someone help? I am finding it very difficult to write Unit tests for Scala classes as well as mocking them.
Scalatest versions from Build.sbt:
"org.scalatestplus" %% "play" % "1.2.0" % "test",
"org.scalamock" %% "scalamock-scalatest-support" % "3.2" % "test",
Try wrapping the invocation in parens
(t.stage1(_: String)).when(token).returns(testToken)
I believe scalac thinks you're trying to call when on an instance of String (since that would be what's returned by t.stage1(_)).