Кэширование Circe неявно разрешено экземпляров Encoder / Decoder - PullRequest
0 голосов
/ 29 января 2019

Я использую 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 Ответ

0 голосов
/ 29 января 2019

Почему вы не можете сделать это вообще

Это невозможно, поскольку то, что вы просите, сводится к кешированию 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]]
  }
  // ...

}
...