首页 > 解决方案 > F多态代码中如何使用guave缓存加载器

问题描述

我有一项服务,它 从官方示例中返回笑话:

  final case class JokeError(e: Throwable) extends RuntimeException

  def impl[F[_] : Sync](C: Client[F]): Jokes[F] = new Jokes[F] {
    val dsl = new Http4sClientDsl[F] {}
    import dsl._
    def get: F[Jokes.Joke] = {
      C.expect[Joke](GET(uri"https://icanhazdadjoke.com/"))
        .adaptError { case t => JokeError(t) }
    }
  }

但我想用番石榴缓存缓存第一个请求的笑话(只是通过常量键,这没关系):

object Jokes {
  def apply[F[_]](implicit ev: Jokes[F]): Jokes[F] = ev

  final case class Joke(joke: String) extends AnyRef
  object Joke {
    implicit val jokeDecoder: Decoder[Joke] = deriveDecoder[Joke]
    implicit def jokeEntityDecoder[F[_]: Sync]: EntityDecoder[F, Joke] =
      jsonOf
    implicit val jokeEncoder: Encoder[Joke] = deriveEncoder[Joke]
    implicit def jokeEntityEncoder[F[_]: Applicative]: EntityEncoder[F, Joke] =
      jsonEncoderOf
  }

  final case class JokeError(e: Throwable) extends RuntimeException

  def impl[F[_]: Sync](C: Client[F]): Jokes[F] = new Jokes[F]{

    val cacheLoader : CacheLoader[String, Joke] = new CacheLoader[String, Joke] {
      override def load(key: String): Joke = {
        import dsl._
        val joke: F[Joke] = C.expect[Joke](GET(uri"https://icanhazdadjoke.com/"))
          .adaptError{ case t => JokeError(t)}
        //? F[Joke] => Joke
        null
      }
    }

    val cache = CacheBuilder.newBuilder().build(cacheLoader)

    val dsl = new Http4sClientDsl[F]{}

    def get: F[Jokes.Joke] = {
       //it's ok?
       cache.get("constant").pure[F]
    }
  }
}

如您所见,cacheLoader需要“物化”值F[Joke] => Joke。并且缓存返回纯值而不F

如何在F多态代码中使用此缓存?

标签: scalascala-catshttp4scats-effect

解决方案


您基本上是在询问如何在 中运行多态代码F,为此您需要EffectF.

此外pure,您需要使用而不是使用delay,因为从缓存中获取值是一种副作用。

val cacheLoader : CacheLoader[String, Joke] = new CacheLoader[String, Joke] {
  override def load(key: String): Joke = {
    import dsl._
    val joke: F[Joke] = C.expect[Joke](GET(uri"https://icanhazdadjoke.com/"))
      .adaptError{ case t => JokeError(t)}

    // This is a side effect, but can't avoid it due to the way the API is designed
    joke.toIO.unsafeRunSync()
  }
}

val cache = CacheBuilder.newBuilder().build(cacheLoader)

val dsl = new Http4sClientDsl[F]{}

def get: F[Jokes.Joke] = {
   // This is okay :)
   Sync[F].delay(cache.get("constant"))
}

顺便说一句,如果你想使用与 http4s 互操作性非常好的东西,我强烈推荐mules. 在这里查看: https ://github.com/ChristopherDavenport/mules


推荐阅读