英文:
How to pass into generic type using a Symbol or Type object into a generic typed function?
问题
在Scala中,是否可以将从Symbol或Type对象派生的类型传递给一个泛型函数?例如:
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
def a[T: TypeTag](): Unit = {
val fields: Seq[Symbol] = typeOf[T].members.filter(_.isMethod == false).toSeq
fields.foreach(x => {
b[x.getMyType]() // 如何将字段的"Type"传递给泛型类型的函数?
})
}
def b[T](): Unit = ???
a[Person]()
从上面的示例中,我希望在调用a[Person]()
时,在a()内部使用反射获取Person中的字段,然后使用每个字段的类型调用b[?]()
。
英文:
In Scala, is it possible to pass a type derived from a Symbol or Type object into a generic typed function? For example:
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
def a[T: TypeTag](): Unit = {
val fields: Seq[Symbol] = typeOf[T].members.filter(_.isMethod == false).toSeq
fields.foreach(x => {
b[x.getMyType]() // How to pass field's "Type" into generic typed function?
})
}
def b[T](): Unit = ???
a[Person]()
From the above example, I am interested in calling a[Person]()
and within a(), use reflection to obtain the fields from Person to make calls into b[?]()
using each field's type.
答案1
得分: 3
以下是您要翻译的内容:
是否可以将从Symbol或Type对象派生的类型传递给泛型类型的函数?
方法b
的类型参数T
必须在编译时知道,但x.typeSignature
只在运行时知道。
您可以尝试使用编译时反射而不是运行时反射。然后,x.typeSignature
将在宏的运行时(主要代码的编译时)变为已知。
// 宏子项目
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def a[T](): Unit = macro aImpl[T]
def aImpl[T: c.WeakTypeTag](c: blackbox.Context)(): c.Tree = {
import c.universe._
val fields: Seq[Symbol] = weakTypeOf[T].members.filter(_.isMethod == false).toSeq
val bCalls = fields.map(x =>
q"b[${x.typeSignature}]()"
)
q"..$bCalls"
}
// 主要子项目
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
def b[T](): Unit = ???
a[Person]()
// scalac: {
// b[App.Address]();
// b[Int]();
// b[String]()
//}
类似的事情也可以使用Shapeless完成。
import shapeless.ops.hlist.{FillWith, Mapper}
import shapeless.{Generic, HList, Poly0, Poly1}
def b[T](): Unit = println("b")
object bPoly extends Poly1 {
implicit def cse[X]: Case.Aux[X, Unit] = at(_ => b[X]())
}
object nullPoly extends Poly0 {
implicit def cse[X]: Case0[X] = at(null.asInstanceOf[X])
}
def a[T] = new PartiallyAppliedA[T]
class PartiallyAppliedA[T] {
def apply[L <: HList]()(implicit
generic: Generic.Aux[T, L],
mapper: Mapper[bPoly.type, L],
fillWith: FillWith[nullPoly.type, L]
): Unit = mapper(fillWith())
}
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
a[Person]()
//b
//b
//b
或者,如果您确实想使用运行时反射,您必须推迟b[...]()
的编译直到运行时。您可以使用toolbox实现这一点。
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
def a[T: TypeTag](): Unit = {
val fields: Seq[Symbol] = typeOf[T].members.filter(_.isMethod == false).toSeq
val bCalls = fields.map(x =>
q"b[${x.typeSignature}]()"
)
toolbox.eval(q"""
import Obj._
..$bCalls
""")
}
object Obj {
def b[T](): Unit = println("b")
}
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
a[Person]()
//b
//b
//b
英文:
> is it possible to pass a type derived from a Symbol or Type object into a generic typed function?
Type parameter T
of method b
must be known at compile time but x.typeSignature
becomes known only at runtime.
You can try to use compile-time reflection rather than runtime one. Then x.typeSignature
becomes known at runtime of the macro, which is compile time of the main code.
// macros subproject
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def a[T](): Unit = macro aImpl[T]
def aImpl[T: c.WeakTypeTag](c: blackbox.Context)(): c.Tree = {
import c.universe._
val fields: Seq[Symbol] = weakTypeOf[T].members.filter(_.isMethod == false).toSeq
val bCalls = fields.map(x =>
q"b[${x.typeSignature}]()"
)
q"..$bCalls"
}
// main subproject
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
def b[T](): Unit = ???
a[Person]()
// scalac: {
// b[App.Address]();
// b[Int]();
// b[String]()
//}
Similar thing can be done with Shapeless.
import shapeless.ops.hlist.{FillWith, Mapper}
import shapeless.{Generic, HList, Poly0, Poly1}
def b[T](): Unit = println("b")
object bPoly extends Poly1 {
implicit def cse[X]: Case.Aux[X, Unit] = at(_ => b[X]())
}
object nullPoly extends Poly0 {
implicit def cse[X]: Case0[X] = at(null.asInstanceOf[X])
}
def a[T] = new PartiallyAppliedA[T]
class PartiallyAppliedA[T] {
def apply[L <: HList]()(implicit
generic: Generic.Aux[T, L],
mapper: Mapper[bPoly.type, L],
fillWith: FillWith[nullPoly.type, L]
): Unit = mapper(fillWith())
}
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
a[Person]()
//b
//b
//b
Alternatively, if you really want to use runtime reflection, you have to postpone compilation of b[...]()
till runtime. You can do this with toolbox.
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
def a[T: TypeTag](): Unit = {
val fields: Seq[Symbol] = typeOf[T].members.filter(_.isMethod == false).toSeq
val bCalls = fields.map(x =>
q"b[${x.typeSignature}]()"
)
toolbox.eval(q"""
import Obj._
..$bCalls
""")
}
object Obj {
def b[T](): Unit = println("b")
}
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
a[Person]()
//b
//b
//b
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论