我使用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?
发布于 2019-01-29 13:44:17
为什么你不能泛泛地这么做
这是不可能的,因为您所请求的内容归结为缓存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]的值会破坏它的“功能”。
下一个最好的事情
第二个最好的方法是手动缓存一堆类型的实例。为了避免样板文件,我推荐使用Shapeless的cachedImplicit宏。对于您的解码器示例,您最终会得到:
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 {
// ...
}
}如果您不喜欢宏,您可以手动执行此操作(基本上就是无形宏所做的操作),但它可能没有那么有趣。这使用了一个鲜为人知的技巧,隐含的可以通过隐藏他们的名字来“隐藏”。
package json extends JsonCodec {
implicit val strDecoder: Decoder[String] = {
def strDecoder = ???
implicitly[Decoder[String]]
}
implicit val intDecoder: Decoder[Int] = {
def intDecoder = ???
implicitly[Decoder[Int]]
}
// ...
}https://stackoverflow.com/questions/54411094
复制相似问题