找不到 ContextShift 的隐式值

Cannot find an implicit value for ContextShift

我正在尝试使用基于 Http4sServlet 的 http4s 创建 webapp。 以下代码无法编译:

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)

错误信息:

[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

UserSvcServer实现如下:

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)
  }
}

如何隐式导入 ContextShift?

上下文转换只是猫对 ExecutionContext 的包装。您可以在文档中明确创建一个 stated

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

您通常会在应用程序的入口点创建一个上下文转换(可能在 main 方法中)。

如果您的应用使用来自 cats-effect 的 IOApp,那么它已经隐含了范围内的 contextShift。它将使用执行上下文,其线程数等于您计算机的可用处理器。

如果您想在应用程序中使用创建的 contextShift "deeper",您可以将其作为隐式参数传递:

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

因此,为了使您的代码正常工作,您需要确保方法或 class 调用 UserSvcServlet 的构造函数具有隐含的 contextShift: (implicit cs: ContextShift[IO]).

您也可以将其放在单独的对象中:

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

}

然后需要contextShift的时候导入即可:

import AppContextShift._

顺便说一下,为 Blocker 使用全局执行上下文不是个好主意。 Blocked 用于阻塞操作,将其与 ExecutionContext.global 一起使用可能会导致您的应用程序中的线程不足。

最常见的方法是使用从缓存线程池创建的阻塞程序:

Blocker.liftExecutorService(Executors.newCachedThreadPool())