问题描述
我是 Scala 2 宏的初学者(在我切换到 Dotty 之前),他在尝试了无形类型类派生后想要更进一步,编写一个可以为任何 {{1} 生成类型类实例的宏没有它。
(为了举例,让我们忽略嵌套的递归类型,所以我的目标是扁平案例类。)
我的类型类是一个抽象类 Coder[T]
(例如带有 scala.Product
/ encode()
的 trait)。
decode()
应该是这样的:
case class Pojo(
s: String,i: Int,l: List[Int]
)
(删除了完全指定的类名以提高可读性)
在宏中我尝试:
- 检查
import com.github.fpopic.scalamacros.Pojo import org.apache.beam.sdk.coders.Coder import java.io.{ByteArrayInputStream,ByteArrayOutputStream} import java.util class PojoCoder extends Coder[Pojo] { import com.github.fpopic.scalamacros.beam.defmacroCoder.{ stringCoder,intCoder,listCoder } override def encode(value: Pojo,os: OutputStream): Unit = { stringCoder.encode(value.s,os) intCoder.encode(value.i,os) listCoder(intCoder).encode(value.l,os) } override def decode(is: InputStream): Pojo = { Pojo( s = stringCoder.decode(is),i = intCoder.decode(is),l = listCoder(intCoder).decode(is) ) } override def getCoderArguments: util.List[_ <: Coder[_]] = { Collections.emptyList() } override def verifyDeterministic(): Unit = () }
- 迭代每个案例类构造函数字段(
weakTypeOf[P]
类型)
Coder[P]
但是调用 def materializeProductCoder[P: c.WeakTypeTag](c: blackBox.Context): c.Expr[Coder[P]] = {
import c.universe._
val tpe = c.weakTypeOf[P]
val helper = new MacrosHelper[c.type](c)
val expressions =
helper.getPrimaryConstructorMembers(tpe).map { field =>
val fieldTerm = field.asTerm.name // e.g. value.s (for Now just s)
val fieldType = field.typeSignature.finalResultType // e.g. String
val fieldCoderName = c.freshName(TermName("coder")) // e.g. give friendly name coder$...
val fieldCoderInstance = // e.g. finds instance of Coder[String]
c.typecheck(
tree = q"""_root_.scala.Predef.implicitly[org.apache.beam.sdk.coders.Coder[${fieldType}]]""",silent = false
)
val fieldCoderExpression =
q"private val ${fieldCoderName}: org.apache.beam.sdk.coders.Coder[${fieldType}] = ${fieldCoderInstance}"
val fieldEncodeExpression =
q"${fieldCoderName}.encode(value.${fieldTerm},os)" // replace with full relative name (with dots) instead of value
val fieldDecodeExpression =
q"${field.asTerm} = ${fieldCoderName}.decode(is)"
(fieldCoderExpression,fieldEncodeExpression,fieldDecodeExpression)
}
val fieldCodersExpression = expressions.map(_._1).distinct
val coderEncodeExpresions = expressions.map(_._2)
val coderDecodeExpresions = expressions.map(_._3)
val coderExpression =
q"""{
new org.apache.beam.sdk.coders.Coder[${tpe}] {
{import ${c.prefix}._}
..${fieldCodersExpression}
override def encode(value: ${tpe},os: java.io.OutputStream): _root_.scala.Unit = {
..${coderEncodeExpresions}
}
override def decode(is: java.io.InputStream): ${tpe} = {
${tpe.typeConstructor}(
..${coderDecodeExpresions}
)
}
override def getCoderArguments: java.util.List[_ <: org.apache.beam.sdk.coders.Coder[_]] = {
java.util.Collections.emptyList
}
override def verifyDeterministic(): _root_.scala.Unit = ()
}
}
"""
val ret = coderExpression
c.Expr[Coder[P]](ret)
}
后出现错误:
(在导入和隐式搜索方面有点挣扎,所以现在有中间 sbt Test / compile
,而 distinct 没用)
private val
{
final class $anon extends org.apache.beam.sdk.coders.Coder[com.github.fpopic.scalamacros.beam.Pojo] {
def <init>() = {
super.<init>();
()
};
{
import defmacroCoder._;
()
};
private val coder$macro$1: org.apache.beam.sdk.coders.Coder[String] = scala.Predef.implicitly[org.apache.beam.sdk.coders.Coder[String]](defmacroCoder.stringCoder);
private val coder$macro$2: org.apache.beam.sdk.coders.Coder[Int] = scala.Predef.implicitly[org.apache.beam.sdk.coders.Coder[Int]](defmacroCoder.intCoder);
private val coder$macro$3: org.apache.beam.sdk.coders.Coder[List[Int]] = scala.Predef.implicitly[org.apache.beam.sdk.coders.Coder[List[Int]]](defmacroCoder.listCoder[Int](defmacroCoder.intCoder));
override def encode(value: com.github.fpopic.scalamacros.beam.Pojo,os: java.io.OutputStream): _root_.scala.Unit = {
coder$macro$1.encode(value.s,os);
coder$macro$2.encode(value.i,os);
coder$macro$3.encode(value.l,os)
};
override def decode(is: java.io.InputStream): com.github.fpopic.scalamacros.beam.Pojo = com.github.fpopic.scalamacros.beam.Pojo(s = coder$macro$1.decode(is),i = coder$macro$2.decode(is),l = coder$macro$3.decode(is));
override def getCoderArguments: java.util.List[_$1] forSome {
<synthetic> type _$1 <: org.apache.beam.sdk.coders.Coder[_$2] forSome {
<synthetic> type _$2
}
} = java.util.Collections.emptyList;
override def verifyDeterministic(): _root_.scala.Unit = ()
};
new $anon()
}
我相信哪个来自 here 但不完全理解编译器试图告诉我什么?
可以找到完整代码示例的链接here
可以找到指向 CI 错误的链接 here。
解决方法
您实例化类的方式是错误的:
${tpe.typeConstructor}(...)
应该是
new $tpe(...)
或者如果你想用 case 类伴随对象的 apply
而不是普通的构造函数来做到这一点:
${tpe.typeSymbol.companion}(...)
注意:类型构造函数(也称为高级类型)与类构造函数
无关