How to compose two Http4s routes with zio effect and different types of environment

Viewed 441

I have two Http4s routes:

val routes1:HttpRoutes[Task] = ???
val routes2:HttpRoutes[RTask] = ???

Where RTask is just a Task/RIO with a custom environment:

type RTask[A] = RIO[Env,A]

Composing of two routes with the same type parameters can be accomplished by importing "zio-cats-interop" library and doing regular routes1<+>routes1, but because the type parameter of HttpRoutes is invariant, I can't do the same for different types:

routes1<+>routes2 /// error here

Is there any solution for this?

1 Answers

Let's dealias things:

type HttpRoutes[F[_]] = Http[OptionT[F, *], F]
type Http[F[_], G[_]] = Kleisli[F, Request[G], Response[G]]

so your HttpRoutes[RTask] is in fact Kleisli[OptionT[RTask, *], Request[RTask], Response[RTask]].

Let's say you have applyR: RTask ~> Task (which applies R) and requireR: Task ~> RTask (which adds R that won't be used).

val applyR: RTask ~> Task = new (RTask ~> Task) {
  def apply[A](rtask: RTask[A]): Task[A] = ... // apply Env here
}
val requireR: Task ~> RTask = new (Task ~> RTask) {
  def apply[A](task: Task[A]): RTask[A] = ... // require Env here
}

You could adapt each of Kleisli's: input, output, effects using them, although it will be a chore:

val routesRTask: HttpRoutes[RTask]

val routesTask: HttpRoutes[Task] =
  Kleisli { (reqTask: Request[Task]) =>
    val reqRTask: Request[RTask] = req.mapK(requireR)
    val responseRTask: OptionT[RTask, Response[RTask]] = routesRTask(reqTask)
    responseRTask.mapK(applyR) // OptionT[Task, Response[RTask]]
                 .map(_.mapK(applyR)) // OptionT[Task, Response[Task]]
  }

You could extract the logic above to some function and apply it to all routes which should be converted from RTask to Task.

Related