I have created simple own type:
type MyEnv = ZEnv with Clock with MyRepository
and have Main method:
object Main extends App {
lazy val live: ZLayer[Any, Nothing, Has[MyRepository.Service]] = ZLayer.succeed(LiveMyRepository.repository)
val routes = Router[AppTask](
"/" -> MyRoutes.routes
).orNotFound
val server: ZIO[MyEnv, Throwable, Unit] = ZIO.runtime[MyEnv]
.flatMap {
implicit rts =>
BlazeServerBuilder[AppTask]
.bindHttp(config.port, config.host)
.withHttpApp(routes)
.serve
.compile
.drain
}
def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = {
server.provideCustomLayer(live) foldM(
err => putStrLn(s"Execution failed with: $err") *> IO.succeed(1),
_ => IO.succeed(0))
}
But I got compilation error in run method:
Error:(33, 30) Cannot prove that zio.ZEnv with zio.Has[my.repositories.MyRepository.Service] <:< my.application.Environment.MyEnv.
server.provideCustomLayer(live) foldM(
I do not know how to fix it. Also I have error connected with implicit in server.provideCustomLayer(live):
No parameter found for implicits ev: ZEnv with Has[MyRepository.Service] <:< MyEnv
I tried to find a solution, but ZIO documentation is so useless and there is no good solution in other places.
Related
So I was writing a simple program in ZIO which would ask user its name and then simply greet him/her.
object PrintName extends App {
val askName: ZIO[Any, Throwable, ZIO[Console, IOException, Unit]] = for {
name: String <- ZIO.effect(StdIn.readLine())
} yield (putStrLn(s"Hello $name"))
override def run(args: List[String]): URIO[ZEnv, ExitCode] = {
...
}
}
What should I write inside the run method so that I can execute the putStrLn effect?
val askName: ZIO[Any, Throwable, ZIO[Console, IOException, Unit]]
Why do you have a type like that? It means that when the effect askName is run, it will produce another effect (that you would also need to run in order for it to have an effect; sorry for the pun).
You will want to use flatMap (or some variation of it, like the <- in a for comprehension) to combine the two effects.
val askName: ZIO[Console, Throwable, Unit]] = for {
name <- ZIO.effect(StdIn.readLine())
_ <- putStrLn(s"Hello $name")
} yield ()
or
val askName: ZIO[Console, Throwable, Unit]] =
ZIO.effect(StdIn.readLine())
.flatMap(name => putStrLn(s"Hello $name"))
Imagine I have OptionT[IO, Value] like this
case class FailureMsg(code: String, ex: Option[Throwable])
val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
.map { value ⇒
println("Mapping over")
value
}
.flatMapF[Int](_ ⇒ IO.raiseError(new RuntimeException("err1")))
.toRight(FailureMsg("Code0", None))
.recoverWith {
case ex ⇒ // Not Throwable!
EitherT.leftT[IO, Int](FailureMsg("Code1", Some(ex)))
}
.value
How can I catch err1 and wrap it into Left[FailureMsg]. I expected recoverWith help me but surprisingly it's alias of mapLeft. What should I do ?
I wrote helper class to do this.
implicit class EitherTExt[F[_], A, B](val obj: EitherT[F, A, B]) {
def recoverThrowable(pf: PartialFunction[Throwable, Either[A, B]])(implicit A: ApplicativeError[F, Throwable]): EitherT[F, A, B] =
EitherT(obj.value.recover(pf))
}
Let me know if there is more elegant shorter way.
I would follow the types.
val start: OptionT[IO, Int] = OptionT.some[IO](12345)
val thenMap: OptionT[IO, Int] = start.map { value ⇒
println("Mapping over")
value
}
// here it will get off the rails
val thenFlatMapF: OptionT[IO, Int] =
thenMap.flatMapF[Int](_ ⇒ IO.raiseError(new RuntimeException("err1")))
val thenToRight: EitherT[IO, FailureMsg, Int] =
thenFlatMapF.toRight(FailureMsg("Code0", None))
val result: IO[Either[FailureMsg, Int]] = thenToRight.value
thenFlatMapF won't produce OptionT[IO, Int] if IO.raiseError is the case, because no default mapping of Throwable to what? And you will get exception in folding result of IO.raiseError.
First attempt to fix it, will illustrate it:
val thenFlatMapF: OptionT[IO, Int] = thenMap.flatMapF[Int](_ ⇒ {
IO.raiseError[Option[Int]](new RuntimeException("err1")).recoverWith {
case err =>
val result: Option[Int] = ???
IO.pure(result)
}
})
How to handle error in place without breaking IO and return Option so that OptionT[IO, Int] is produced?
So basically, in this case, if you expect flatMapF to fail and need information on error, then it is better to have EitherT as its container, rather than OptionT.
Once done, possible solution shows that at some point leftMap or variance should be done to map the Throwable to FailureMsg. One of reasons is because IO has default error expressed as Throwable. One can't just mix FailureMsg and Throwable. Either inheritance is required so that FailureMsg is of type Throwable/Exception, or mapping should be done of errors in suitable places.
My rough solution would be:
val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
// ok, let's do some input interpretation
.map { value ⇒
println("Mapping over")
value
}
// if no input, means error
.toRight(FailureMsg("Code0", None))
// if there is interpreted input to process, do it and map the errors
.flatMapF(_ ⇒ IO.raiseError[Int](new RuntimeException("err1")).attempt.map(_.leftMap {
case err: RuntimeException if err.getMessage == "err1" => FailureMsg("err1", Some(err))
// just for illustration, that if you have temptation to map on error,
// most likely there won't be only exception
case t: Throwable => FailureMsg("unexpected", Some(t))
}))
.value
However, normally contents of flatMapF would be a separate function or effect that would include error handling. Something like this:
val doJob: Int => IO[Either[FailureMsg, Int]] = arg =>
IO.raiseError[Int](new RuntimeException("err1")).attempt.map(_.leftMap {
case err: RuntimeException if err.getMessage == "err1" => FailureMsg("err1", Some(err))
case t: Throwable => FailureMsg("unexpected", Some(t))
})
val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
.map { value ⇒
println("Mapping over")
value
}
.toRight(FailureMsg("Code0", None))
.flatMapF(arg ⇒ doJob(arg))
.value
I have the following code:
override def getStandsByUser(email: String): Try[Seq[Stand]] =
(for {
user <- OptionT(userService.findOneByEmail(email)): Try[Option[User]]
stands <- OptionT.liftF(standService.list()):[Try[List[Stand]]]
filtered = stands.filter(stand => user.stands.contains(stand.id))
} yield filtered).getOrElse(Seq())
}
I want to add logging on each stage of the processing - so I need to introduce writer monad and stack it with monad transformer OptionT. Could you please suggest how to do that?
The best way to do this is to convert your service calls into using cats-mtl.
For representing Try or Option you can use MonadError and for logging you can use FunctorTell. Now I don't know what exactly you're doing inside your userService or standService, but I wrote some code to demonstrate what the result might look like:
type Log = List[String]
//inside UserService
def findOneByEmail[F[_]](email: String)
(implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[User] = ???
//inside StandService
def list[F[_]]()
(implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[List[Stand]] = ???
def getStandsByUser[F[_]](email: String)
(implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[List[Stand]] =
for {
user <- userService.findOneByEmail(email)
stands <- standService.list()
} yield stands.filter(stand => user.stands.contains(stand.id))
//here we actually run the function
val result =
getStandsByUser[WriterT[OptionT[Try, ?], Log, ?] // yields WriterT[OptionT[Try, ?], Log, List[Stand]]
.run // yields OptionT[Try, (Log, List[Stand])]
.value // yields Try[Option[(Log, List[Stand])]]
This way we can avoid all of the calls to liftF and easily compose our different services even if they will use different monad transformers at runtime.
If you take a look at the definition of cats.data.Writer you will see that it is an alias to cats.data.WriterT with the effect fixed to Id.
What you want to do is use WriterT directly and instead of Id use OptionT[Try, YourType].
Here is a small code example of how that can be achieved:
object Example {
import cats.data._
import cats.implicits._
type MyType[A] = OptionT[Try, A]
def myFunction: MyType[Int] = OptionT(Try(Option(1)))
def main(args: Array[String]): Unit = {
val tmp: WriterT[MyType, List[String], Int] = for {
_ <- WriterT.tell[MyType, List[String]](List("Before first invocation"))
i <- WriterT.liftF[MyType, List[String], Int](myFunction)
_ <- WriterT.tell[MyType, List[String]](List("After second invocation"))
j <- WriterT.liftF[MyType, List[String], Int](myFunction)
_ <- WriterT.tell[MyType, List[String]](List(s"Result is ${i + j}"))
} yield i + j
val result: Try[Option[(List[String], Int)]] = tmp.run.value
println(result)
// Success(Some((List(Before first invocation, After second invocation, Result is 2),2)))
}
}
The type annotations make this a bit ugly, but depending on your use case you might be able to get rid of them. As you can see myFunction returns a result of type OptionT[Try, Int] and WriterT.lift will push that into a writer object that also has a List[String] for your logs.
In this snippet y.run doesn't typecheck.
object Test {
type StateStringTask[A] = StateStringT[Task, A]
type StateStringT[M[_], A] = StateT[M, String, A]
val x: Process[Task, Unit] = ???
val y: Process[StateStringTask, Unit] = ???
x.run // This typechecks
y.run // This fails
}
The compiler shows this error:
could not find implicit value for parameter C: scalaz.Catchable[[x]Test.StateStringTask[x]]
Do I have to create a Catchable instance for StateStringTask? How do I do that? Or is there an easier way to handle stateful effects when running a Process?
I guess this is suboptimal but I got it by making StateStringTask an instance of Catchable:
implicit val stateStringTaskInstance: Catchable[StateStringTask] =
new Catchable[StateStringTask] {
// `a.attempt` stackoverflows, don't ask me why :)
def attempt[A](a: StateStringTask[A]): StateStringTask[Throwable \/ A] = a >>= (
x => Catchable[Task].attempt(Applicative[Task].pure(x)).liftM[StateStringT]
)
def fail[A](err: Throwable) = Catchable[Task].fail(err).liftM[StateStringT]
}
To hoist the StateT on a Process with aTask as effect. For example:
def received(queue: Queue[Event]): Process[StateStringTask, Event] = {
val toStateStringTask = new (Task ~> StateStringTask) {
def apply[A](t: Task[A]): StateStringTask[A] = t.liftM[StateStringT]
}
// queue.dequeue: Process[Task, Event]
queue.dequeue.translate(toStateStringTask)
}
I'm using spray-client to generate http requests to my server in e2e tests. I also use specs2 to test for the desired response from the server. And everything works fine.
I've built some custom specs2 matchers to simplify my test code. My test looks like this:
val response = get(<server_endpoint_url>)
response must beSuccessfulWith(content = expected_data)
I have a trait that somewhat simplifies the usage of spray in the test itself:
trait SprayTestClientSupport {
implicit val system = ActorSystem()
import system.dispatcher // execution context for futures
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
def get(url: String): Future[HttpResponse] = pipeline(Get(url))
}
I also have a trait where I define the custom matchers I use in the test:
trait SprayTestClientSupport extends ShouldMatchers with SprayJsonSupport with DefaultJsonProtocol {
def beSuccessfulWith(content: Seq[Int]): Matcher[Future[HttpResponse]] =
beSuccessful and haveBodyWith(content)
def haveBodyWith(content: Seq[Int]): Matcher[Future[HttpResponse]] =
containTheSameElementsAs(content) ^^ { f: Future[HttpResponse] =>
Await.result(f, await).entity.as[Seq[Int]].right.get
}
def beSuccessful: Matcher[Future[HttpResponse]] =
===(StatusCode.int2StatusCode(200)) ^^ { f: Future[HttpResponse] =>
Await.result(f, await).status
}
}
My problem starts when I try to make the matchers more general and support any Scala type for instance. I define something like this:
def haveBodyWith[T: TypeTag](content: T): Matcher[Future[HttpResponse]] =
===(content) ^^ { f: Future[HttpResponse] =>
Await.result(f, await).entity.as[T].right.get
}
But then I get the following error message:
Error:(49, 86) could not find implicit value for parameter unmarshaller: spray.httpx.unmarshalling.Unmarshaller[T]
===(content) ^^ { (f: Future[HttpResponse]) => { Await.result(f, await).entity.as[T].right.get } }
Is there anything simple that I'm missing?
Thanks!
P.S.
I use the following spray versions:
spray-client_2.10 -> 1.3.3
spray-can_2.10 -> 1.3.3
spray-http_2.10 -> 1.3.3
spray-httpx_2.10 -> 1.3.3
spray-util_2.10 -> 1.3.3
spray-json_2.10 -> 1.3.2
You need to add a constraint to your T parameter
def haveBodyWith[T: TypeTag : Unmarshaller](content: T): Matcher[Future[HttpResponse]] =
===(content) ^^ { f: Future[HttpResponse] =>
Await.result(f, await).entity.as[T].right.get
}