Репозиторий git, содержащий проблему, можно найти здесь https://github.com/mdedetrich/scalacache-example

Проблема, с которой я столкнулся в настоящее время, заключается в том, что я пытаюсь сделать свой бэкэнд-сервер ScalaCache независимым, чтобы его можно было настраивать во время выполнения с использованием конфигурации типа безопасного типа.

Проблема, с которой я столкнулся, заключается в том, что ScalaCache параметризует конструкторы кеша, то есть для создания кеша кофеина вы бы сделали

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 знает, как сериализовать любой тип T. CaffeineCache использует CacheRepr, который равен InMemoryRepr, тогда как SentinelRedisCache использует CacheRepr, который равен Array[Byte].

И здесь суть проблемы в том, что у меня есть Config, который просто хранит, какой кеш используется, т.е.

import scalacache.Cache
import scalacache.caffeine.CaffeineCache
import scalacache.redis.SentinelRedisCache

final case class ApplicationConfig(cache: Cache[_])

Причина, по которой это Cache[_], заключается в том, что во время компиляции мы не знаем, какой кеш используется, ApplicationConfig будет создан во время выполнения с помощью CaffeineCache / SentinelRedisCache.

И здесь суть проблемы в том, что Scala не может найти неявный Codec для типа подстановочного знака, если мы просто используем applicationConfig.cache в качестве конструктора, то есть https://github.com/mdedetrich/scalacache-example/blob/master /src/main/scala/Main.scala#L17

Если мы раскомментируем строку выше, мы получим

[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

Кто-нибудь знает, как решить эту проблему, по сути, я хочу указать, что в моем ApplicationConfig кеш имеет тип Cache[InMemoryRepr | Array[Byte]], а не просто Cache[_] (чтобы компилятор Scala знал, что нужно искать последствия для InMemoryRepr or Array[Byte] и для MyCache должны быть определены примерно так

final case class MyCache[CacheRepr <: InMemoryRepr | Array[Byte]](scalaCache: ScalaCache[CacheRepr])
0
mdedetrich 5 Окт 2018 в 14:50

1 ответ

Лучший ответ

Кажется, вы просите компилятор разрешить неявные значения на основе выбора типа кеша во время выполнения. Это невозможно, потому что к моменту запуска кода приложения компилятор больше не работает.

Вы должны сделать так, чтобы разрешение типа происходило во время компиляции, а не во время выполнения. Таким образом, вам нужно определить 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.

2
Tim 5 Окт 2018 в 13:11