我有一个带有签名的宏,例如
def generateSomething[A] = macro ...
也就是说,它接受类型参数。该类型应为case类,因此apply
在其伴随对象中始终具有相应的方法。
这个宏以及其他所有东西都会生成此apply
方法的调用,因此,例如,对于此类:
case class A(x: Int, y: String)
将生成以下调用:
A.apply(someFunction[Int], someFunction[String])
我someFunction
从apply
签名中提取用于调用的参数类型。
除非A
参数化,否则一切都很好:
case class A[T](x: Int, y: T)
使用我当前的方法,将为以下内容生成以下内容generateSomething[A[String]]
:
A.apply[String](someFunction[Int], someFunction[T])
这显然是无效的。
但是,在知道apply
所有类型参数之后,我不知道如何获取。也就是说,我不知道如何确保
generateSomething[A[String]]
产生
A.apply[String](someFunction[Int], someFunction[String])
而不是上面的那一块。是否有可能?
更新
我认为我应该重新表述这个问题。
假设有一堂课
case class A[T1, ..., Tn](x1: A1, ..., xm: Am)
哪里Ai
可以取决于的任意子集Tk
。例子:
// T1 = T
// A1 = Int, A2 = T
case class B[T](x: Int, y: T)
// T1 = U, T2 = V
// A1 = Map[U, V], A2 = List[V]
case class C[U, V](m: Map[U, V], l: List[V])
// T1 = W
// A1 = W, A2 = W
case class D[W](t: W, u: W)
// No Ts
// A1 = String, A2 = Double
case class E(v: String, w: Double) // no type parameters at all
我需要编写一个接受类型参数的宏,A
并扩展为A.apply
具有预处理参数的方法调用:
myMacro[A[U1, ..., Un]]
// expands to
A.apply[U1, ..., Un](preprocess[A1], ..., preprocess[An])
Uk
这是代替的实际类型参数Tk
。例如(使用上面的类):
myMacro[B[String]] -> B.apply[String](preprocess[Int], preprocess[String])
myMacro[C[Int, Double]] -> C.apply[Int, Double](preprocess[Map[Int, Double]], preprocess[List[Double]])
myMacro[D[Long]] -> D.apply[Long](preprocess[Long], preprocess[Long])
myMacro[E] -> D.apply(preprocess[String], preprocess[Double])
您会看到,apply
参数类型可以取决于类型参数。尽管这些参数是宏已知的(因为它总是用具体的类型调用),但我不知道如何将这些参数“传递”给apply
函数,以使preprocess
类型参数正确。
更新2
这是我目前所拥有的。
就像是:
case class X[A](a: A)
object TParamMacro {
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
def m[A](): A = macro mImpl[A]
def mImpl[A: c.WeakTypeTag](c: Context)(): c.Expr[A] = {
import c.universe._
val TypeRef(pre, sym, args) = weakTypeTag[A].tpe
val t = args.head
val expr =
if (t <:< typeOf[String]) q"""X.apply[$t]("hi")"""
else if (t <:< typeOf[Int]) q"X.apply[$t](42)"
else q"X.apply[$t](null)"
c.Expr[A](expr)
}
}
object Test extends App {
Console println TParamMacro.m[X[String]]()
}
更多示例:
object TParamMacro {
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
def m[A](): Any = macro mImpl[A]
def mImpl[A: c.WeakTypeTag](c: Context)() = {
import c.universe._
val TypeRef(pre, sym, args) = weakTypeTag[A].tpe
val t = args.head
val expr = if (t <:< typeOf[String]) q"""X.apply[List[$t]](List.apply[$t]("hi"))"""
else if (t <:< typeOf[Int]) q"X.apply[List[$t]](List.apply[$t](42))"
else q"X.apply[List[$t]](Nil)"
expr
}
}
在哪里
Console println TParamMacro.m[X[String]]()
产量
X(List(hi))
修复要点后进行编辑:
package evaluator
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
object Evaluator {
def preprocess[T]: T = ???
def evaluate[A]: Any = macro evaluateImpl[A]
def evaluateImpl[A: c.WeakTypeTag](c: Context): c.Expr[A] = {
import c.universe._
val tpe = weakTypeOf[A]
val sym = tpe.typeSymbol.asClass
require(sym.isCaseClass)
val companionSym = sym.companion
val companionTpe = companionSym.typeSignature
val applyMethod = companionTpe.member(TermName("apply")).asMethod
val paramTypes = applyMethod.paramLists.flatten.map(_.typeSignature)
Console println s"apply($paramTypes)"
val TypeRef(_, _, tpeTypeArgs) = tpe
val from = applyMethod.typeParams
val to = tpeTypeArgs
val arguments = paramTypes map { t =>
val u = if (from.nonEmpty) t.substituteTypes(from, to) else t
Console println s"param is $t, subst is $u"
q"evaluator.Evaluator.preprocess[$u]"
}
c.Expr(q"$companionSym.apply[..$tpeTypeArgs](..$arguments)")
}
}
因此,您只是将“实际类型args”替换为方法的类型参数。在应用程序中,将“ parameter”用于形式参数,将“ argument”用于实际arg是很有用的。
样本:
package evaluator
case class A(x: Int, y: String)
case class B[T](x: Int, y: T)
case class C[U, T](x: Int, y: T, z: U)
object Test extends App {
Evaluator.evaluate[A]
Evaluator.evaluate[B[String]]
Evaluator.evaluate[C[String, List[Int]]]
}
使用
-Xprint:typer
然后
A.apply(evaluator.Evaluator.preprocess[Int], evaluator.Evaluator.preprocess[String]);
B.apply[String](evaluator.Evaluator.preprocess[Int], evaluator.Evaluator.preprocess[String]);
C.apply[String, List[Int]](evaluator.Evaluator.preprocess[Int], evaluator.Evaluator.preprocess[List[Int]], evaluator.Evaluator.preprocess[String])
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句