从宏获取带有匿名类方法的结构类型 [英] Getting a structural type with an anonymous class's methods from a macro

查看:103
本文介绍了从宏获取带有匿名类方法的结构类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们要编写一个宏,该宏使用一些类型成员或方法定义一个匿名类,然后使用这些方法创建该类的实例,该实例被静态类型化为结构类型,等等.这在宏系统中是可能的在2.10.0中,类型成员部分非常简单:

Suppose we want to write a macro that defines an anonymous class with some type members or methods, and then creates an instance of that class that's statically typed as a structural type with those methods, etc. This is possible with the macro system in 2.10.0, and the type member part is extremely easy:

object MacroExample extends ReflectionUtils {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context

  def foo(name: String): Any = macro foo_impl
  def foo_impl(c: Context)(name: c.Expr[String]) = {
    import c.universe._

    val Literal(Constant(lit: String)) = name.tree
    val anon = newTypeName(c.fresh)

    c.Expr(Block(
      ClassDef(
        Modifiers(Flag.FINAL), anon, Nil, Template(
          Nil, emptyValDef, List(
            constructor(c.universe),
            TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
          )
        )
      ),
      Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
    ))
  }
}

(其中ReflectionUtils是提供我的constructor方法的便利性.)

(Where ReflectionUtils is a convenience trait that provides my constructor method.)

此宏使我们可以将匿名类的类型成员的名称指定为字符串文字:

This macro lets us specify the name of the anonymous class's type member as a string literal:

scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6

请注意,它是正确键入的.我们可以确认一切都按预期进行:

Note that it's appropriately typed. We can confirm that everything's working as expected:

scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>

现在假设我们尝试使用一种方法来做同样的事情:

Now suppose that we try to do the same thing with a method:

def bar(name: String): Any = macro bar_impl
def bar_impl(c: Context)(name: c.Expr[String]) = {
  import c.universe._

  val Literal(Constant(lit: String)) = name.tree
  val anon = newTypeName(c.fresh)

  c.Expr(Block(
    ClassDef(
      Modifiers(Flag.FINAL), anon, Nil, Template(
        Nil, emptyValDef, List(
          constructor(c.universe),
          DefDef(
            Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
            c.literal(42).tree
          )
        )
      )
    ),
    Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
  ))
}

但是当我们尝试时,我们没有得到结构类型:

But when we try it out, we don't get a structural type:

scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492

但是,如果我们在其中添加一个额外的匿名类:

But if we stick an extra anonymous class in there:

def baz(name: String): Any = macro baz_impl
def baz_impl(c: Context)(name: c.Expr[String]) = {
  import c.universe._

  val Literal(Constant(lit: String)) = name.tree
  val anon = newTypeName(c.fresh)
  val wrapper = newTypeName(c.fresh)

  c.Expr(Block(
    ClassDef(
      Modifiers(), anon, Nil, Template(
        Nil, emptyValDef, List(
          constructor(c.universe),
          DefDef(
            Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
            c.literal(42).tree
          )
        )
      )
    ),
    ClassDef(
      Modifiers(Flag.FINAL), wrapper, Nil,
      Template(Ident(anon) :: Nil, emptyValDef, constructor(c.universe) :: Nil)
    ),
    Apply(Select(New(Ident(wrapper)), nme.CONSTRUCTOR), Nil)
  ))
}

有效:

scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834

scala> res0.test
res1: Int = 42

这非常方便-例如,它可以让您执行之类的事情,但是我不知道无法理解它为什么起作用,并且类型成员版本起作用,但bar却不起作用.我知道可能没有定义的行为,但这有意义吗?有没有一种更干净的方法可以从宏中获取结构类型(带有方法)?

This is extremely handy—it lets you do things like this, for example—but I don't understand why it works, and the type member version works, but not bar. I know this may not be defined behavior, but does it make any sense? Is there an cleaner way to get a structural type (with the methods on it) from a macro?

推荐答案

Travis在此处重复回答了这个问题. .在跟踪器和Eugene的讨论中都有链接(在评论和邮件列表中).

This question is answered in duplicate by Travis here. There are links to the issue in the tracker and to Eugene's discussion (in the comments and mailing list).

在类型检查器中著名的"Skylla and Charybdis"部分中,我们的英雄决定了什么应摆脱黑暗的匿名性,并将光视为结构类型的成员.

In the famous "Skylla and Charybdis" section of the type checker, our hero decides what shall escape dark anonymity and see the light as a member of the structural type.

有两种方法可以欺骗类型检查器(这并不意味着Odysseus会抱羊).最简单的方法是插入一个伪语句,以使该块看起来不像是带有其实例化的匿名类.

There are a couple of ways to trick the type checker (which do not entail Odysseus's ploy of hugging a sheep). The simplest is to insert a dummy statement so that the block doesn't look like an anonymous class followed by its instantiation.

如果打字员注意到您是外界未引用的公共术语,则会使您成为私人.

If the typer notices that you're a public term that isn't referenced by the outside, it will make you private.

object Mac {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context

  /* Make an instance of a structural type with the named member. */
  def bar(name: String): Any = macro bar_impl

  def bar_impl(c: Context)(name: c.Expr[String]) = {
    import c.universe._
    val anon = TypeName(c.freshName)
    // next week, val q"${s: String}" = name.tree
    val Literal(Constant(s: String)) = name.tree
    val A    = TermName(s)
    val dmmy = TermName(c.freshName)
    val tree = q"""
      class $anon {
        def $A(i: Int): Int = 2 * i
      }
      val $dmmy = 0
      new $anon
    """
      // other ploys
      //(new $anon).asInstanceOf[{ def $A(i: Int): Int }]
      // reference the member
      //val res = new $anon
      //val $dmmy = res.$A _
      //res
      // the canonical ploy
      //new $anon { }  // braces required
    c.Expr(tree)
  }
}

这篇关于从宏获取带有匿名类方法的结构类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆