【问题标题】:Obtaining the realised type for a Scala Class using macros使用宏获取 Scala 类的实现类型
【发布时间】:2017-02-05 06:23:15
【问题描述】:

我在使用 scala 宏和识别构造函数的实现类型时遇到了一些问题。 不确定我是否在这里做错了什么或正确的调用是什么。 从文档来看,typeSignatureIn 应该返回正确的信息,例如ClassTag[Int],但是当我运行宏时,我实际上得到了无法编译的 ClassTag[U],因为 U 是类型参数,而不是实现的类型。

import scala.language.experimental.macros
import scala.reflect.ClassTag
import scala.reflect.macros.Context

def macroImpl[T: c.WeakTypeTag](c: Context) = {
  import c.universe._

  val typeToMock = weakTypeOf[T]

  val primaryConstructorOpt = typeToMock.members.collectFirst {
    case method: MethodSymbolApi if method.isPrimaryConstructor => method
  }

  val constructorArgumentsTypes = primaryConstructorOpt.map { 
    constructor =>
    val constructorTypeContext = constructor.typeSignatureIn(typeToMock)
    val constructorArguments = constructor.paramss
    constructorArguments.map { symbols =>
      symbols.map(_.typeSignatureIn(constructorTypeContext))
    }
  }

  println(typeToMock)
  println(constructorArgumentsTypes)

  c.literalUnit
}

def foo[T] = macro macroImpl[T]

class Foo[U: ClassTag]

foo[Foo[Int]]

运行它:

scala> foo[Foo[Int]]
Foo[Int]
Some(List(List(), List(scala.reflect.ClassTag[U]))

我需要以某种方式获取 ClassTag[Int] 以便以后能够生成正确的树,有什么想法吗?

【问题讨论】:

    标签: scala scala-macros


    【解决方案1】:

    尝试在您解析类型的每个位置使用dealias。 Scala 会保留引用,即使它知道它们所指的是什么。 dealias 为您获取已替换引用的类型的副本 (?)。

    您还应该选择黑盒或白盒宏,而不仅仅是scala.reflect.macros.Context

    这似乎有效:

    import scala.language.experimental.macros
    import scala.reflect.ClassTag
    import scala.reflect.macros.whitebox.Context
    
    def macroImpl[T: c.WeakTypeTag](c: Context) = {
      import c.universe._
    
      val typeToMock = weakTypeOf[T].dealias
    
      val primaryConstructorOpt = typeToMock.members.collectFirst {
        case method: MethodSymbolApi if method.isPrimaryConstructor => method
      }
    
      val constructorArgumentsTypes = primaryConstructorOpt.map { constructor =>
        val constructorTypeContext = constructor.typeSignatureIn(typeToMock).dealias
        val constructorArguments = constructorTypeContext.paramLists
        constructorArguments.map { symbols =>
          symbols.map(_.typeSignatureIn(constructorTypeContext).dealias)
        }
      }
    
      println(typeToMock)
      println(constructorArgumentsTypes)
    
      q"()"
    }
    
    def foo[T]: Any = macro macroImpl[T]
    
    class Foo[U: ClassTag]
    
    foo[Foo[Int]]
    

    结果

    Foo[Int]
    Some(List(List(), List(scala.reflect.ClassTag[Int])))
    

    【讨论】:

    • 非常感谢。仍在努力将其集成到更广泛的代码库中,但它给了我一个很好的线索,让我接下来要调查什么
    猜你喜欢
    • 2022-09-30
    • 1970-01-01
    • 1970-01-01
    • 2015-04-03
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 2013-06-14
    相关资源
    最近更新 更多