Не удается найти неявное значение для ContextShift - PullRequest
2 голосов
/ 29 мая 2020

Я пытаюсь создать веб-приложение с http4s, основанное на Http4sServlet. Следующий код не компилируется:

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 неявно?

1 Ответ

2 голосов
/ 29 мая 2020

Сдвиг контекста - это просто кошачья обертка над ExecutionContext. Вы можете создать его явно как указано в документах:

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

Обычно вы создаете один сдвиг контекста в точке входа вашего приложения (возможно, в основном методе).

Если ваше приложение использует IOApp from cats-effect, это уже будет неявно для contextShift в области видимости. Он будет использовать контекст выполнения, который будет иметь количество потоков, равное доступным процессорам вашего компьютера.

Если вы хотите использовать созданный contextShift «глубже» внутри вашего приложения, вы можете передать его как неявный параметр:

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

Итак, чтобы ваш код работал, вам необходимо убедиться, что конструктор вызовов метода или класса 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())

...