Working with ZIO and http4s given a non-empty ZIO environment

Hello everyone,

I’m trying to create a simple server that has some dependencies and I’m following the instructions here, replacing * with _ as I’m using Scala 3 (I believe that’s correct, but not really sure). This is the code, with comments regarding the compiler errors.

object Http4sMain extends ZIOAppDefault {
  object endpoints {
    object root {
      val root: ZServerEndpoint[Any, Any] =
        endpoint.get.out(stringBody).serverLogicSuccess(_ => ZIO.succeed("empty"))

      val info: ZServerEndpoint[Indexing & Monitoring, Any] =
        endpoint.get.in("info").out(stringBody).serverLogicSuccess(_ => ZIO.succeed("info"))

      val health: ZServerEndpoint[Any, Any] =
        endpoint.get.in("health").serverLogicSuccess(_ => ZIO.unit)

      val all = List(info.widen[Env], health.widen[Env], root.widen[Env])
    }
  }

  type Env = MyConfig & Indexing & Monitoring
  // fails to compile with error "Type argument ZIO[Env, Throwable, ?]
  // does not have the same kind as its bound"
  val routes: HttpRoutes[RIO[Env, _]] = ZHttp4sServerInterpreter().from(endpoints.root.all).toRoutes

  def run = ZIO.executor.flatMap { executor =>
    BlazeServerBuilder[Task] // Neither Task nor RIO[Env, _] compile, probably because the error above
      .withExecutionContext(executor.asExecutionContext)
      .bindHttp(8000, "0.0.0.0")
      .withHttpApp(Router("/" -> routes).orNotFound)
      .resource
      .use { server =>
        for {
          _ <- Console.printLine(
            s"Server started at http://0.0.0.0:${server.address.getPort}. Press ENTER key to exit."
          )
          _ <- Console.readLine
        } yield ()
      }
  }
}

EDIT: Doing this:

type Effect[+A] = RIO[Env, A]

val routes: HttpRoutes[Effect] = ZHttp4sServerInterpreter().from(endpoints.root.all).toRoutes

and then using Effect as F for BlazeServerBuilder does compile, so this brings up two questions:

  1. What did I do wrong when trying to mimic the kind projector in Scala 3?
  2. How and where do I .provide the dependencies to my effects?

Any help is greatly appreciated! :pray:

According to the migration guide, indeed you should replace * with _. I don’t have a scala 3 + zio + tapir project at hand to verify, though.

As for providing dependencies, the ZIO.executor.flatMap { ... } should yield a ZIO effect with the environment requiring your dependencies, so that’s where you’d provide them. See this example. So I think this would be something like ZIO.executor.flatMap { ... }.provide(...).

To debug the typechecking issue, can you maybe create a self-contained example project (e.g. with the help of adopt-tapir, or just paste in a code snippet), so that I could debug locally?

Hi Adam,

I made a self-contained Scastie here.

Thanks for the help :slightly_smiling_face:

I have no idea why (maybe a Scala issue?) but the following works:

  type X[A] = RIO[Int, A]
  val routes: HttpRoutes[X] = ZHttp4sServerInterpreter().from(List(dummyEndpoint)).toRoutes

Maybe it would be good to report it somewhere in Dotty (if you do, please link back :slight_smile: ).
We are returning a HttpRoutes[RIO[R with Clock with Blocking, *]] using the Scala 2 notation so this should carry over to Scala 3 I guess … but apparently it doesn’t

1 Like

Thanks for taking a look! I’ll open an issue in Dotty when I can :slight_smile:

1 Like

After doing some more reading here, I added the -Ykind-projector compiler flag and now RIO[Env, *] works correctly in Scala 3.2.

I also tried adding the .provide where you said, and it works like a charm! Thanks! :slight_smile:

1 Like