如何通过使用符号或类型对象将内容传递给泛型类型的函数?

huangapple go评论77阅读模式
英文:

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 =&gt; 
    q&quot;b[${x.typeSignature}]()&quot;
  )
  q&quot;..$bCalls&quot;
}

// 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(&quot;b&quot;)

object bPoly extends Poly1 {
  implicit def cse[X]: Case.Aux[X, Unit] = at(_ =&gt; 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 &lt;: 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 =&gt; 
    q&quot;b[${x.typeSignature}]()&quot;
  )
  toolbox.eval(q&quot;&quot;&quot;
    import Obj._
    ..$bCalls
  &quot;&quot;&quot;)
}

object Obj {
  def b[T](): Unit = println(&quot;b&quot;)
}

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

huangapple
  • 本文由 发表于 2020年8月14日 09:13:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/63405194.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定