使用 ToolBox 时隐式解析失败
Implicit resolution fail in reflection with ToolBox
我正在尝试根据 class 路径在反射中生成 Avro4s 的 RecordFormat。以下代码会引发错误。
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]
记录格式就像
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)
}
}
我明白了,它可以解析 Encoder[MyCaseClass]
和 SchemaFor[MyCaseClass]
但无法解析 Decoder[MyCaseClass]
。
相同的代码可以解析 RecordFormat[MyCaseClass]
而无需反射。
我可以看到 Decoder
是用类似于 Encoder
的宏实现的。
implicit def applyMacro[T <: Product]: Decoder[T] = macro applyMacroImpl[T]
为什么反射不能解决隐含证据?
avro4s
4.x 使用 Magnolia but avro4s
2.x uses raw implicit macros + Shapeless.
通常 there shouldn't be significant problems 在运行时使用反射工具箱实现类型 class,即使类型 class 是用宏定义的。
现在的问题是定义 com.sksamuel.avro4s.Decoder
的宏有一个错误。 Decoder.scala#L404
行
c.Expr[Decoder[T]](
q"""
new _root_.com.sksamuel.avro4s.Decoder[$tpe] {
private[this] val decoders = Array(..$decoders)
override def decode(value: Any, schema: _root_.org.apache.avro.Schema): $tpe = {
val fullName = $fullName
value match {
case record: _root_.org.apache.avro.generic.GenericRecord => $companion.apply(..$fields)
case _ => sys.error("This decoder decodes GenericRecord => " + fullName + " but has been invoked with " + value)
}
}
}
"""
)
指的是sys.error
而不是hygienic _root_.scala.sys.error
.
如果您修复此行,Decoder[MyCaseClass]
和 RecordFormat[MyCaseClass]
将在工具箱中工作
println(toolBox.compile {
toolBox.parse(
s"""
|import com.sksamuel.avro4s._
|import mypackage.MyCaseClass
|RecordFormat[MyCaseClass]
|""".stripMargin
)
}()) //com.sksamuel.avro4s.RecordFormat$$anon@25109d84
所以一个快速的解决方法是删除行
libraryDependencies += "com.sksamuel.avro4s" %% "avro4s-core" % "2........."
在build.sbt
中添加
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.3"
libraryDependencies += "org.apache.avro" % "avro" % "1.8.2"
(否则你会得到 NoClassDefFoundError
)并将以下修补的 jar 放入 lib
https://github.com/DmytroMitin/avro4s-2.0.5-2.11-patched
avro4s-core_2.11-2.0.5-SNAPSHOT.jar
avro4s-macros_2.11-2.0.5-SNAPSHOT.jar
您始终可以调试使用工具箱生成的基于隐式或基于宏的代码,如果您像这样创建它的话
val toolBox = runtimeMirror.mkToolBox(
frontEnd = new FrontEnd {
override def display(info: Info): Unit = println(info)
override def interactive(): Unit = ???
},
options = "-Xlog-implicits" // or "-Xlog-implicits -Ymacro-debug-lite"
)
如果你这样做
println(reify{
Decoder[MyCaseClass]
}.tree)
它打印
Decoder.apply[MyCaseClass](Decoder.applyMacro)
所以隐式 Decoder[MyCaseClass]
被解析为 Decoder.applyMacro[MyCaseClass]
。
使用原始未打补丁的 jar
toolBox.compile {
toolBox.parse(
s"""
|import com.sksamuel.avro4s._
|import mypackage.MyCaseClass
|Decoder.applyMacro[MyCaseClass]
|""".stripMargin
)
}()
抛出
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
object error is not a member of package sys
我正在尝试根据 class 路径在反射中生成 Avro4s 的 RecordFormat。以下代码会引发错误。
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]
记录格式就像
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)
}
}
我明白了,它可以解析 Encoder[MyCaseClass]
和 SchemaFor[MyCaseClass]
但无法解析 Decoder[MyCaseClass]
。
相同的代码可以解析 RecordFormat[MyCaseClass]
而无需反射。
我可以看到 Decoder
是用类似于 Encoder
的宏实现的。
implicit def applyMacro[T <: Product]: Decoder[T] = macro applyMacroImpl[T]
为什么反射不能解决隐含证据?
avro4s
4.x 使用 Magnolia but avro4s
2.x uses raw implicit macros + Shapeless.
通常 there shouldn't be significant problems 在运行时使用反射工具箱实现类型 class,即使类型 class 是用宏定义的。
现在的问题是定义 com.sksamuel.avro4s.Decoder
的宏有一个错误。 Decoder.scala#L404
c.Expr[Decoder[T]](
q"""
new _root_.com.sksamuel.avro4s.Decoder[$tpe] {
private[this] val decoders = Array(..$decoders)
override def decode(value: Any, schema: _root_.org.apache.avro.Schema): $tpe = {
val fullName = $fullName
value match {
case record: _root_.org.apache.avro.generic.GenericRecord => $companion.apply(..$fields)
case _ => sys.error("This decoder decodes GenericRecord => " + fullName + " but has been invoked with " + value)
}
}
}
"""
)
指的是sys.error
而不是hygienic _root_.scala.sys.error
.
如果您修复此行,Decoder[MyCaseClass]
和 RecordFormat[MyCaseClass]
将在工具箱中工作
println(toolBox.compile {
toolBox.parse(
s"""
|import com.sksamuel.avro4s._
|import mypackage.MyCaseClass
|RecordFormat[MyCaseClass]
|""".stripMargin
)
}()) //com.sksamuel.avro4s.RecordFormat$$anon@25109d84
所以一个快速的解决方法是删除行
libraryDependencies += "com.sksamuel.avro4s" %% "avro4s-core" % "2........."
在build.sbt
中添加
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.3"
libraryDependencies += "org.apache.avro" % "avro" % "1.8.2"
(否则你会得到 NoClassDefFoundError
)并将以下修补的 jar 放入 lib
https://github.com/DmytroMitin/avro4s-2.0.5-2.11-patched
avro4s-core_2.11-2.0.5-SNAPSHOT.jar
avro4s-macros_2.11-2.0.5-SNAPSHOT.jar
您始终可以调试使用工具箱生成的基于隐式或基于宏的代码,如果您像这样创建它的话
val toolBox = runtimeMirror.mkToolBox(
frontEnd = new FrontEnd {
override def display(info: Info): Unit = println(info)
override def interactive(): Unit = ???
},
options = "-Xlog-implicits" // or "-Xlog-implicits -Ymacro-debug-lite"
)
如果你这样做
println(reify{
Decoder[MyCaseClass]
}.tree)
它打印
Decoder.apply[MyCaseClass](Decoder.applyMacro)
所以隐式 Decoder[MyCaseClass]
被解析为 Decoder.applyMacro[MyCaseClass]
。
使用原始未打补丁的 jar
toolBox.compile {
toolBox.parse(
s"""
|import com.sksamuel.avro4s._
|import mypackage.MyCaseClass
|Decoder.applyMacro[MyCaseClass]
|""".stripMargin
)
}()
抛出
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
object error is not a member of package sys