如何使用Symbol或Type对象将其传递给泛型类型的函数?

问题描述

在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]() // How to pass field's "Type" into generic typed function?
    })
}

def b[T](): Unit = ???

a[Person]()

在上面的示例中,我感兴趣于调用a[Person](),并且在a()中,使用反射从Person中获取字段,以使用每个字段的类型调用b[?]()

解决方法

是否可以将从Symbol或Type对象派生的类型传递给泛型类型的函数?

在编译时必须知道方法T的类型参数b,但是x.typeSignature仅在运行时才知道。

您可以尝试使用compile-time reflection而不是运行时之一。然后x.typeSignature在宏的运行时即主要代码的编译时就知道了。

// 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]()
//}

类似的事情可以用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,fillWith: FillWith[nullPoly.type,L]
  ): Unit = mapper(fillWith())
}

case class Address(street: String,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,address: Address)

a[Person]()

//b
//b
//b