Search code examples
scalaimplicitscala-catshttp4scats-effect

Cannot find an implicit value for ContextShift


I am trying to create webapp with http4s that is based on Http4sServlet. The following code does not compile:

import cats.effect._
import org.http4s.servlet.BlockingServletIo
import org.http4s.servlet.Http4sServlet
import scala.concurrent.ExecutionContext.global
import org.http4s.implicits._


class UserSvcServlet
  extends Http4sServlet[IO](service = UserSvcServer.start
    , servletIo = BlockingServletIo(4096, Blocker.liftExecutionContext(global)))(IOApp)

the error message:

[error] /home/developer/scala/user-svc/src/main/scala/io/databaker/UserSvcServlet.scala:12:54: Cannot find implicit value for ConcurrentEffect[[+A]cats.effect.IO[A]].
[error] Building this implicit value might depend on having an implicit
[error] s.c.ExecutionContext in scope, a Scheduler, a ContextShift[[+A]cats.effect.IO[A]]
[error] or some equivalent type.
[error]   extends Http4sServlet[IO]( service = UserSvcServer.stream
[error]                                                      ^
[error] /home/developer/scala/user-svc/src/main/scala/io/databaker/UserSvcServlet.scala:13:36: Cannot find an implicit value for ContextShift[[+A]cats.effect.IO[A]]:
[error] * import ContextShift[[+A]cats.effect.IO[A]] from your effects library
[error] * if using IO, use cats.effect.IOApp or build one with cats.effect.IO.contextShift
[error]     , servletIo = BlockingServletIo(4096, Blocker.liftExecutionContext(global)))
[error]                                    ^
[error] two errors found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed May 29, 2020, 8:45:00 PM

The UserSvcServer is implemented as follows:

import org.http4s.HttpApp
import cats.effect.{ConcurrentEffect, ContextShift, Timer}
import org.http4s.implicits._
import org.http4s.server.middleware.Logger


object UserSvcServer {

  def start[F[_] : ConcurrentEffect](implicit T: Timer[F], C: ContextShift[F]): HttpApp[F] = {
    val helloWorldAlg = HelloWorld.impl[F]
    val httpApp = UserSvcRoutes.helloWorldRoutes[F](helloWorldAlg).orNotFound
    Logger.httpApp(true, true)(httpApp)
  }
}

How can I import ContextShift implicitly?


Solution

  • Context shift is just cats' wrapper over ExecutionContext. You can create one explicitly as stated in docs:

    implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
    

    You usually create one context shift at the entry point of your app (probably in the main method).

    If your app uses IOApp from cats-effect it would have already implicit for contextShift in scope. It will use execution context, which would have number of threads equal to available processors of your computer.

    If you want to use created contextShift "deeper" inside your application you can pass it as an implicit parameter:

    def doSomething(implicit cs: ContextShift[IO]): IO[Unit] = ???
    

    So in order to make your code work, you need to make sure that method or class calls constructor of UserSvcServlet has implicit for contextShift: (implicit cs: ContextShift[IO]).

    You could also put it in separate object:

    object AppContextShift {
    
      implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
      implicit val t: Timer[IO] = IO.timer(ExecutionContext.global) //you will probably also need timer eventually
    
    }
    

    Then you can import it when contextShift is needed:

    import AppContextShift._
    

    By the way, using a global execution context for Blocker is not good idea. Blocked is used for blocking operations and using it with ExecutionContext.global might lead to thread starvation in your app.

    The most common approach is to use blocker created from cached thread pool:

    Blocker.liftExecutorService(Executors.newCachedThreadPool())