Bazı tür üyeler veya yöntemlerle anonim bir sınıf tanımlayan ve daha sonra bu yöntemlerle vb. Statik olarak yapısal tür olarak yazılan bir sınıf örneği oluşturan bir makro yazmak istediğimizi varsayalım. Bu, 2.10'daki makro sistemiyle mümkündür. 0 ve tür üye kısmı son derece kolaydır:
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)
))
}
}
(Nerede ReflectionUtils
bir olan kolaylık özellik benim sağlayan constructor
yöntem.)
Bu makro, anonim sınıfın tür üyesi adını dize hazır bilgisi olarak belirtmemizi sağlar:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Uygun şekilde yazıldığını unutmayın. Her şeyin beklendiği gibi çalıştığını doğrulayabiliriz:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
Şimdi, aynı şeyi bir yöntemle yapmaya çalıştığımızı varsayalım:
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)
))
}
Ancak denediğimizde yapısal bir tür elde etmiyoruz:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
Ama orada fazladan anonim bir sınıf yapıştırırsak:
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)
))
}
İşe yarıyor:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
Bu son derece kullanışlı-it gibi şeyler yapalım olan bu için, örneğin-ama işe yarar ve tipi üyesi versiyonu çalışır, ancak neden anlamıyorum bar
. Bunun tanımlanmış bir davranış olmayabileceğini biliyorum , ama bir anlamı var mı? Bir makrodan yapısal bir tür (üzerindeki yöntemlerle) almanın daha temiz bir yolu var mı?