gpt4 book ai didi

scala - 如何在为类型实现的 Scala 宏中使用 ClassTag

转载 作者:行者123 更新时间:2023-12-01 09:39:32 41 4
gpt4 key购买 nike

我写了一个宏,它读取类字段:

import scala.language.experimental.macros
import scala.reflect.macros.whitebox

object ArrayLikeFields {
def extract[T]: Set[String] = macro extractImpl[T]

def extractImpl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[Set[String]] = {

import c.universe._

val tree = weakTypeOf[T].decls
.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}
.map(y => y.paramLists.headOption.getOrElse(Seq.empty))
.getOrElse(Seq.empty)
.map(s => q"${s.name.decodedName.toString}")

c.Expr[Set[String]] {
q"""Set(..$tree)"""
}
}

}

我能够为具体类型编译和运行它:

object Main extends App {
case class Person(name:String)
val res: Set[String] = ArrayLikeFields.extract[Person]
}

但我想将它与这样的通用类型一起使用:

object Lib {
implicit class SomeImplicit(s: String) {

def toOrgJson[T]: JSONObject = {
val arrayLikeFields: Set[String] = ArrayLikeFields.extract[T]
//some code, that uses fields, etc
null
}
}
}

编译错误:

Error:(14, 65) type mismatch; found : scala.collection.immutable.Set[Nothing] required: Set[String] Note: Nothing <: String, but trait Set is invariant in type A. You may wish to investigate a wildcard type such as _ <: String. (SLS 3.2.10) val arrayLikeFields: Set[String] = ArrayLikeFields.extract[T]

我不明白。我该如何解决我的问题?

更新
我读了scala 2.10.2 calling a 'macro method' with generic type not work关于物化,但我没有类的实例

最佳答案

尝试实现类型类的方法,如 1

object Main extends App {
case class Person(name:String)
val res: Set[String] = ArrayLikeFields.extract[Person] //Set(name)

import Lib._
"abc".toOrgJson[Person] // prints Set(name)
}

object Lib {
implicit class SomeImplicit(s: String) {
def toOrgJson[T: ArrayLikeFields.Extract]: JSONObject = {
val arrayLikeFields: Set[String] = ArrayLikeFields.extract[T]
//some code, that uses fields, etc
println(arrayLikeFields) //added
null
}
}
}

import scala.language.experimental.macros
import scala.reflect.macros.whitebox

object ArrayLikeFields {
def extract[T](implicit extr: Extract[T]): Set[String] = extr()

trait Extract[T] {
def apply(): Set[String]
}

object Extract {
implicit def materializeExtract[T]: Extract[T] = macro materializeExtractImpl[T]

def materializeExtractImpl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[Extract[T]] = {
import c.universe._

val tree = weakTypeOf[T].decls
.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}
.map(y => y.paramLists.headOption.getOrElse(Seq.empty))
.getOrElse(Seq.empty)
.map(s => q"${s.name.decodedName.toString}")

c.Expr[Extract[T]] {
q"""new ArrayLikeFields.Extract[${weakTypeOf[T]}] {
override def apply(): _root_.scala.collection.immutable.Set[_root_.java.lang.String] =
_root_.scala.collection.immutable.Set(..$tree)
}"""
}
}
}
}

实际上,我认为这里不需要白框宏,黑框宏就足够了。因此,您可以将 (c: whitebox.Context) 替换为 (c: blackbox.Context)

顺便说一句,同样的问题可以用 Shapeless 而不是宏来解决(宏在 Shapeless 中工作)

object Main extends App {
case class Person(name:String)
val res: Set[String] = ArrayLikeFields.extract[Person] //Set(name)
}

object ArrayLikeFields {
def extract[T: Extract]: Set[String] = implicitly[Extract[T]].apply()

trait Extract[T] {
def apply(): Set[String]
}

object Extract {
def instance[T](strs: Set[String]): Extract[T] = () => strs

implicit def genericExtract[T, Repr <: HList](implicit
labelledGeneric: LabelledGeneric.Aux[T, Repr],
extract: Extract[Repr]
): Extract[T] = instance(extract())

implicit def hconsExtract[K <: Symbol, V, T <: HList](implicit
extract: Extract[T],
witness: Witness.Aux[K]
): Extract[FieldType[K, V] :: T] =
instance(extract() + witness.value.name)

implicit val hnilExtract: Extract[HNil] = instance(Set())
}
}

关于scala - 如何在为类型实现的 Scala 宏中使用 ClassTag,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52392071/

41 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com