Кэширование Circe неявно разрешено экземпляров Encoder/Decoder
Я использую Circe для сериализации / десериализации некоторых достаточно больших моделей, где каждое поле листа имеет строгий тип (например, case class FirstName(value: String) extends AnyVal
).
Неявное разрешение / вывод Encoder
или же Decoder
медленный.
У меня есть свой собственный кодек, для которого я добавляю некоторые дополнительные Encoder
а также Decoder
экземпляры:
trait JsonCodec extends AutoDerivation {
// ...
}
С помощью следующего метода, чтобы помочь с декодированием:
package json extends JsonCodec {
implicit class StringExtensions(val jsonString: String) extends AnyVal {
def decodeAs[T](implicit decoder: Decoder[T]): T =
// ...
}
}
Проблема в том, что каждый раз, когда я звоню decodeAs
неявно выводит Decoder
что приводит к значительному увеличению времени компиляции.
Есть ли способ, которым я могу (в общем) кешировать последствия так, что он будет генерировать только Decoder
один раз?
1 ответ
Почему вы не можете сделать это в общем
Это невозможно, поскольку то, что вы просите, сводится к кешированию def
, Частично проблема заключается в том, что создание неявного экземпляра может (хотя и редко) имеет побочные эффекты. Патологический пример:
scala> var myVar: Int = 0
myVar: Int = 0
scala> :paste
// Entering paste mode (ctrl-D to finish)
trait DummyTypeclass[T] { val counter: Int }
implicit def dummyInstance[T]: DummyTypeclass[T] = {
myVar += 1
new DummyTypeclass[T] {
val counter = myVar
}
}
// Exiting paste mode, now interpreting.
defined trait DummyTypeclass
dummyInstance: [T]=> DummyTypeclass[T]
scala> implicitly[DummyTypeclass[Int]].count
res1: Int = 1
scala> implicitly[DummyTypeclass[Boolean]].counter
res2: Int = 2
scala> implicitly[DummyTypeclass[Int]].counter
res3: Int = 3
Как видите, кеширование значения DummyTypeclass[Int]
сломал бы его "функциональность".
Следующая лучшая вещь
Следующая лучшая вещь - это ручное кэширование экземпляров для нескольких типов. Чтобы избежать шаблонов, я рекомендую cachedImplicit
макрос от Shapeless. В качестве примера вашего декодера вы получите:
package json extends JsonCodec {
import shapeless._
implicit val strDecoder: Decoder[String] = cachedImplicit
implicit val intDecoder: Decoder[Int] = cachedImplicit
implicit val boolDecoder: Decoder[Boolean] = cachedImplicit
implicit val unitDecoder: Decoder[Unit] = cachedImplicit
implicit val nameDecoder: Decoder[FirstName] = cachedImplicit
// ...
implicit class StringExtensions(val jsonString: String) extends AnyVal {
// ...
}
}
Если вам не нравятся макросы, вы можете сделать это вручную (в основном то, что делает макрос Shapeless), но это может быть не так весело. Это использует малоизвестный трюк, который может скрывать последствия, скрывая их имя.
package json extends JsonCodec {
implicit val strDecoder: Decoder[String] = {
def strDecoder = ???
implicitly[Decoder[String]]
}
implicit val intDecoder: Decoder[Int] = {
def intDecoder = ???
implicitly[Decoder[Int]]
}
// ...
}