Giả sử chúng ta muốn viết một macro xác định một lớp ẩn danh với một số thành viên hoặc phương thức loại, và sau đó tạo một thể hiện của lớp đó được gõ tĩnh như một kiểu cấu trúc với các phương thức đó, v.v. Điều này có thể với hệ thống macro trong 2.10. 0, và phần thành viên loại cực kỳ dễ dàng:
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)
))
}
}
(Đâu ReflectionUtils
là một đặc điểm tiện lợi cung cấp constructor
phương pháp của tôi .)
Macro này cho phép chúng tôi chỉ định tên của thành viên loại của lớp ẩn danh dưới dạng chuỗi ký tự:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Lưu ý rằng nó được gõ một cách thích hợp. Chúng tôi có thể xác nhận rằng mọi thứ đang hoạt động như mong đợi:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
Bây giờ giả sử rằng chúng ta cố gắng làm điều tương tự với một phương thức:
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)
))
}
Nhưng khi chúng tôi dùng thử, chúng tôi không nhận được loại cấu trúc:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
Nhưng nếu chúng ta gắn thêm một lớp ẩn danh ở đó:
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)
))
}
Nó hoạt động:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
Điều này cực kỳ tiện lợi, nó cho phép bạn làm những việc như thế này , ví dụ như nhưng tôi không hiểu tại sao nó hoạt động và phiên bản thành viên loại hoạt động, nhưng không bar
. Tôi biết điều này có thể không được xác định hành vi , nhưng nó có ý nghĩa gì không? Có cách nào sạch hơn để có được một kiểu cấu trúc (với các phương thức trên nó) từ một macro không?