I am trying to generate Avro4s's RecordFormat in reflection based on class path. The following code throws an error.
case class MyCaseClass(a: Int)
println(toolBox.compile {
toolBox.parse(
s"""
|import com.sksamuel.avro4s._
|import mypackage.MyCaseClass
|RecordFormat[MyCaseClass]
|""".stripMargin
)
}())
could not find implicit value for evidence parameter of type com.sksamuel.avro4s.Decoder[mypackage.MyCaseClass]
RecordFormat is like
object RecordFormat {
def apply[T <: Product : Encoder : Decoder : SchemaFor]: RecordFormat[T] = apply(AvroSchema[T])
def apply[T <: Product : Encoder : Decoder](schema: Schema): RecordFormat[T] = new RecordFormat[T] {
private val fromRecord = FromRecord[T](schema)
private val toRecord = ToRecord[T](schema)
override def from(record: GenericRecord): T = fromRecord.from(record)
override def to(t: T): Record = toRecord.to(t)
}
}
I can see, it can resolve Encoder[MyCaseClass] and SchemaFor[MyCaseClass] but fails for Decoder[MyCaseClass].
The same code can resolve RecordFormat[MyCaseClass] without reflection.
I can see that Decoder is implemented with macro similar to Encoder.
implicit def applyMacro[T <: Product]: Decoder[T] = macro applyMacroImpl[T]
Why reflection cannot resolve the implicit evidence?
avro4s4.x uses Magnolia butavro4s2.x uses raw implicit macros + Shapeless.Normally there shouldn't be significant problems with materializing a type class at runtime using reflective toolbox even if the type class is defined with macros.
The issue is now that the macro defining
com.sksamuel.avro4s.Decoderhas a bug. The line Decoder.scala#L404refers to
sys.errorinstead of hygienic_root_.scala.sys.error.If you fix this line,
Decoder[MyCaseClass]andRecordFormat[MyCaseClass]will work inside toolboxSo a fast fix is to remove the line
in
build.sbt, add(otherwise you'll have
NoClassDefFoundError) and put the following patched jars intolibhttps://github.com/DmytroMitin/avro4s-2.0.5-2.11-patched
You can always debug implicit-based or macro-based code generated with toolbox if you create it like
If you do
it prints
so implicit
Decoder[MyCaseClass]is resolved asDecoder.applyMacro[MyCaseClass].With the original unpatched jars
threw