2

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?

Bogdan Vakulenko
  • 3,380
  • 1
  • 10
  • 25

1 Answers1

3

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.

Mateusz Kubuszok
  • 24,995
  • 4
  • 42
  • 64
  • thanks for the detailed explanation. I managed to apply env to `HttpRoutes[RTask]` when I actually have env. available, but the problem is that I don't know how to get it in case I want to implement method `def compose[R,E](r1:HttpRoutes[ZIO[R,E,*]], r2:HttpRoutes[ZIO[R,E,*]]):HttpRoutes[ZIO[R,E,*]` – Bogdan Vakulenko Jan 31 '21 at 12:02
  • don't want to wrap the result of the method into ZIO. Just want to make it works a similar way as `<+>` does. – Bogdan Vakulenko Jan 31 '21 at 12:05
  • Your solution does work for me, but not perfectly. – Bogdan Vakulenko Jan 31 '21 at 12:11
  • You can switch `RTask` and `Task` and turn `HttpRoutes[Task]` into `HttpRoutes[RTask]` instead. Since you want to combine them you will have to have `Env` available at some point. – Mateusz Kubuszok Jan 31 '21 at 12:26
  • I'm wondering if I can combine without having "true" `Env` available. My expected result type `HttpRoutes[ZIO[R,E,*]]` also has `Env` in its signature, so my idea is to combine the `Env` along with composing routes. I this case I would be able to apply `Evn` after I compose all routes with different `Env`. – Bogdan Vakulenko Jan 31 '21 at 12:42
  • You can, that what `R` in `ZIO[R,E,A]` in for, it's just that at some point you'll have to take something with `R=Env` and provide that `Env`. – Mateusz Kubuszok Jan 31 '21 at 12:49