通过运行时配置使参数化的ScalaCache通用

时间:2018-10-05 11:50:07

标签: scala implicit type-parameter bounded-wildcard unbounded-wildcard

包含问题的git repo可以在https://github.com/mdedetrich/scalacache-example

中找到

我目前遇到的问题是,我试图使我的ScalaCache后端不可知,因为它可以在运行时使用typesafe config进行配置。

我遇到的问题是ScalaCache参数化了缓存的构造函数,即构造了您要执行的Caffeine缓存

ScalaCache(CaffeineCache())

您将在SentinelRedisCache中做什么

ScalaCache(SentinelRedisCache("", Set.empty, ""))

就我而言,我创建了一个名为MyCache的通用缓存包装器,如下所示

import scalacache.ScalaCache
import scalacache.serialization.Codec

final case class MyCache[CacheRepr](scalaCache: ScalaCache[CacheRepr])(
  implicit stringCodec: Codec[Int, CacheRepr]) {

  def putInt(value: Int) = scalaCache.cache.put[Int]("my_int", value, None)
}

我们需要携带CacheRepr,因为ScalaCache知道如何序列化任何类型的TCaffeineCache使用CacheRepr的{​​{1}},其中InMemoryRepr使用SentinelRedisCache的{​​{1}}。

这是问题的症结所在,我有一个CacheRepr,它仅存储正在使用的缓存,即

Array[Byte]

之所以使用Config是因为在编译时我们不知道正在使用什么缓存,import scalacache.Cache import scalacache.caffeine.CaffeineCache import scalacache.redis.SentinelRedisCache final case class ApplicationConfig(cache: Cache[_]) 将在运行时使用Cache[_] / {{1实例化。 }}。

这就是问题的症结所在,如果我们只是将ApplicationConfig用作构造函数,即https://github.com/mdedetrich/scalacache-example/blob/master/src/main/scala/Main.scala#L17,Scala无法为通配符类型找到隐式CaffeineCache / p>

如果取消注释上面的行,则会得到

SentinelRedisCache

有人知道如何解决此问题,从本质上讲,我想指定在我的Codec中,缓存的类型为applicationConfig.cache,而不仅仅是[error] /Users/mdedetrich/github/scalacache-example/src/main/scala/Main.scala:17:37: Could not find any Codecs for type Int and _$1. Please provide one or import scalacache._ [error] Error occurred in an application involving default arguments. [error] val myCache3: MyCache[_] = MyCache(ScalaCache(applicationConfig.cache)) // This doesn't (这样Scala编译器知道查找ApplicationConfig的隐式变量,并为Cache[InMemoryRepr | Array[Byte]]定义这样的内容

Cache[_]

1 个答案:

答案 0 :(得分:2)

您似乎正在要求编译器根据缓存类型的运行时选择来解析隐式值。这是不可能的,因为在应用程序代码启动时,编译器不再运行。

您必须使类型解析发生在编译时,而不是运行时。因此,您需要定义一个trait,代表到缓存的抽象接口,并提供一个基于ApplicationConfig中的设置返回特定实例的工厂函数。可能看起来像这样(未经测试):

sealed trait MyScalaCache {
  def putInt(value: Int)
}

object MyScalaCache {
  def apply(): MyScalaCache =
    if (ApplicationConfig.useCaffine) {
      MyCache(ScalaCache(CaffeineCache())
    } else {
      MyCache(ScalaCache(SentinelRedisCache("", Set.empty, ""))
    }
}

final case class MyCache[CacheRepr](scalaCache: ScalaCache[CacheRepr]) extends MyScalaCache (
  implicit stringCodec: Codec[Int, CacheRepr]) {

  def putInt(value: Int) = scalaCache.cache.put[Int]("my_int", value, None)
}

MyCache中指定了两个具体实例的情况下,编译器将在编译时解析apply中的隐式。