Tra*_*own 182 macros scala structural-typing scala-2.10 scala-macros
假设我们要编写一个定义具有某些类型成员或方法的匿名类的宏,然后创建该类的实例,该类通过这些方法静态地键入为结构类型等.这可以通过2.10中的宏系统实现. 0,类型成员部分非常容易:
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)
))
}
}
Run Code Online (Sandbox Code Playgroud)
(提供我方法ReflectionUtils的便利特性在哪里constructor.)
这个宏允许我们将匿名类的类型成员的名称指定为字符串文字:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Run Code Online (Sandbox Code Playgroud)
请注意,它是适当的键入.我们可以确认一切都按预期工作:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
Run Code Online (Sandbox Code Playgroud)
现在假设我们尝试使用方法执行相同的操作:
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)
))
}
Run Code Online (Sandbox Code Playgroud)
但是当我们尝试时,我们没有得到结构类型:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
Run Code Online (Sandbox Code Playgroud)
但是如果我们在那里坚持一个额外的匿名课:
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)
))
}
Run Code Online (Sandbox Code Playgroud)
有用:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
Run Code Online (Sandbox Code Playgroud)
这是非常方便,它可以让你做这样的事情本,例如-但我不明白为什么它的工作原理,以及该类型成员版本的作品,但不是bar.我知道这可能不是定义的行为,但是它有意义吗?是否有更简洁的方法从宏中获取结构类型(使用其中的方法)?
Travis 在这里一式两份地回答了这个问题.跟踪器和Eugene讨论中的问题有链接(在评论和邮件列表中).
在类型检查器的着名"Skylla和Charybdis"部分,我们的英雄决定什么将逃避黑暗匿名并将光看作结构类型的成员.
有几种方法可以欺骗类型检查器(这不会引起Odysseus拥抱羊的伎俩).最简单的方法是插入一个虚拟语句,以使该块看起来不像一个匿名类,然后是它的实例化.
如果typer注意到你是一个未被外界引用的公共术语,那么它将使你成为私人.
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)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8576 次 |
| 最近记录: |