scalahttp4sziocats-effect

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


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?


Solution

  • 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.