Misalkan kita ingin menulis makro yang mendefinisikan kelas anonim dengan beberapa tipe anggota atau metode, dan kemudian membuat contoh kelas yang secara statis diketik sebagai tipe struktural dengan metode-metode itu, dll. Ini dimungkinkan dengan sistem makro di 2.10. 0, dan bagian tipe anggota sangat mudah:
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)
))
}
}
(Dimana ReflectionUtils
adalah sifat kenyamanan yang menyediakan saya constructor
metode.)
Makro ini memungkinkan kita menentukan nama anggota tipe kelas anonim sebagai string literal:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Perhatikan bahwa itu diketik dengan tepat. Kami dapat mengonfirmasi bahwa semuanya berfungsi seperti yang diharapkan:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
Sekarang anggaplah kita mencoba melakukan hal yang sama dengan metode:
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)
))
}
Tetapi ketika kami mencobanya, kami tidak mendapatkan tipe struktural:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
Tetapi jika kita memiliki kelas anonim tambahan di sana:
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)
))
}
Berhasil:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
Ini sangat berguna — ini memungkinkan Anda melakukan hal-hal seperti ini , misalnya — tetapi saya tidak mengerti mengapa ini bekerja, dan versi tipe anggota berfungsi, tetapi tidak bar
. Saya tahu ini mungkin bukan perilaku yang didefinisikan , tetapi apakah itu masuk akal? Apakah ada cara yang lebih bersih untuk mendapatkan tipe struktural (dengan metode di atasnya) dari makro?
sumber
Jawaban:
Pertanyaan ini dijawab dalam rangkap dua oleh Travis di sini . Ada tautan ke masalah ini di pelacak dan ke diskusi Eugene (dalam komentar dan milis).
Di bagian "Skylla dan Charybdis" yang terkenal dari pemeriksa tipe, pahlawan kita memutuskan apa yang akan lepas dari anonimitas gelap dan melihat cahaya sebagai anggota tipe struktural.
Ada beberapa cara untuk mengelabui pemeriksa tipe (yang tidak mengharuskan si Odysseus memeluk seekor domba). Yang paling sederhana adalah memasukkan pernyataan dummy sehingga blok tidak terlihat seperti kelas anonim diikuti oleh instantiasinya.
Jika pemberitahuan lebih lanjut bahwa Anda adalah istilah publik yang tidak dirujuk oleh pihak luar, itu akan membuat Anda pribadi.
sumber
new $anon {}
.anon
Tujuan saya yang lain adalah di masa depan saya tidak akan menggunakan makro dengan kuasiquote, atau nama-nama khusus serupa.shapeless.Generic
? Terlepas dari niat terbaik saya untuk memaksaAux
tipe pola pengembalian kompiler menolak untuk melihat melalui tipe struktural.