【问题标题】:Finding implicit method definitions in macro context在宏上下文中查找隐式方法定义
【发布时间】:2015-04-23 20:57:46
【问题描述】:

我了解 Scala 中宏的基本概念,但目前无法完成这项(简单?)工作:

  • 查找编译器当前可见的所有隐式defs/vals,以从给定类型转换为另一种类型。

我希望得到的是 ListMethod 对象或类似的东西。我已经玩过enclosingImplicits,但总是得到一个空列表,不知道下一步该去哪里。

我需要做什么才能获得我正在寻找的列表?

【问题讨论】:

    标签: scala macros scala-macros


    【解决方案1】:

    在上下文中从AB 类型只能有一个隐式(或者你会得到模棱两可的隐式),所以如果你想找到它:

    import reflect.macros.Context, scala.language.experimental.macros
    
    def fImpl(c: Context): c.Expr[Unit] = {
      import c.mirror._         
      println(c.inferImplicitValue(typeOf[Int]))
      c.universe.reify( () )
    }
    def f = macro fImpl
    
    scala> f
    <empty>
    
    scala> implicit val a = 5
    a: Int = 5
    
    scala> f
    $line24.$read.$iw.$iw.$iw.$iw.a
    
    scala> implicit val b = 5
    b: Int = 5
    
    scala> f //result will be empty, but error printed to the log
    error: ambiguous implicit values:
     both value a of type => Int
     and value b of type => Int
     match expected type Int
    <empty>
    

    寻找隐式方法:

    def fImpl(c: Context): c.Expr[Unit] = {
          import c.mirror._       
          println(c.inferImplicitValue(typeOf[String => Int]))        
          c.universe.reify( () )
     }
    def f = macro fImpl
    
    scala> f
    <empty>
    
    scala> implicit def aaa(a: String) = 5
    warning: there was one feature warning; re-run with -feature for details
    aaa: (a: String)Int
    
    scala> "A" : Int
    res10: Int = 5
    
    scala> f
    {
      ((a: String) => $line47.$read.$iw.$iw.$iw.$iw.$iw.$iw.aaa(a))
    }
    

    如果silent参数为false(默认true),则在推理错误时会抛出TypecheckException。因此,您可以对其进行分析以找到模棱两可的隐含列表。

    附:如果类型 B 未知 - 没有(记录的)方法可以使用宏查找所有隐式:openImplicits/enclosingImplicits 只是寻找在宏扩展的上下文中实现的隐式 - 并非全部,存在于语境。 Compiler-plugin 可能会有所帮助,但这并不容易。

    如果您真的决定尝试“编译器插件”方式 - 查找隐含的逻辑已实现 hereHere 您可以找到编译器的 Context(与宏的不同)及其 implicitss 字段,其中包含上下文中的所有隐式(但获得适当的上下文并不是那么简单)。


    我不应该告诉你,但是从宏 Context 提升到编译器级别并做你想做的事有一个棘手且不安全的技巧:

     scala>  def fImpl(c: Context): c.Expr[Unit] = {
     |       val cc = c.asInstanceOf[reflect.macros.contexts.Context]
     |       println(cc.callsiteTyper.context.implicitss.flatten)
     |       c.universe.reify( () )
     |  }
    fImpl: (c: reflect.macros.Context)c.Expr[Unit]
    
    scala> def f = macro fImpl
    
    scala> f //I've defined aaaaaaaa etc. implicits while playing with that
    List(aaaaaaaa: ?, lllllllllllllllllllllzzzz: ?, lllllllllllllllllllll: ?, lllllllllllllllllllll: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, b: ?, a: ?, macros: ?, RuntimeClassTag:
    

    无论如何,您必须分析ImplicitInfo 的列表以获得您正在寻找的隐含,这可能不是微不足道的,正如您从Analizer 的来源中看到的那样,但至少有可能获得近似值结果,这可能适合您的需求。但同样,最好非常非常非常小心,因为您使用的结构是可变的,并且方法不是纯的。而且,正如@Eugene Burmako 所注意到的那样,这个解决方案并没有为您提供伴随对象的隐含。

    【讨论】:

    • 我读到这个问题说源类型是给定的,而目标不是,在这种情况下,我认为没有一个好的解决方案。
    • @Travis Brown 在那种情况下,我什至什么都看不到,除了编译器插件。
    • 我不认为 Context.implicitss 包含来自整个隐式范围的内容。 Iirc,对伴随对象中声明的隐式的查找是单独完成的。此外,信息本身可能非常好,但试图自己理解它可能非常困难,因为在编译器中有一个专门用于此的 1.5kloc 文件。总而言之,您必须亲自试验一下,看看这个 hack 是否适用于您的用例,但一定要检查疯狂的情况,以确保您不会错过任何事情。
    • 另外,像这样的黑客攻击的最大风险是它们肯定不能移植到 scala.meta (或者,很可能,甚至根本不能在 scala.meta 中表达),所以如果你以后打算和scala.meta兼容,那会是个问题。
    • 最后,我不确定你是否可以在编译器插件中实现这个逻辑。词法作用域的信息(包括词法作用域的隐式)仅在typer阶段可用,传统插件无法在那里注入。
    猜你喜欢
    • 2015-03-11
    • 1970-01-01
    • 2019-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多