어떤 타입 멤버 나 메소드로 익명 클래스를 정의한 다음, 그 메소드 등으로 구조적 타입으로 정적으로 타입이 지정된 클래스의 인스턴스를 생성하는 매크로를 작성하려고한다고 가정 해 봅시다. 이것은 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)
))
}
}
( 나의 방법 을 제공 ReflectionUtils
하는 편의 특성 은 어디에 있습니까 constructor
?)
이 매크로를 사용하면 익명 클래스의 형식 멤버 이름을 문자열 리터럴로 지정할 수 있습니다.
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
적절하게 입력되었습니다. 모든 것이 예상대로 작동하는지 확인할 수 있습니다.
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
이제 우리는 방법으로 같은 일을하려고한다고 가정하자 :
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)
))
}
그러나 시도해 볼 때 구조적 유형을 얻지 못합니다.
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
그러나 거기에 익명 클래스를 추가하면
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
. 나는 이것이 행동으로 정의되지는 않았지만 어떤 의미가 있는지 알고 있습니다 . 매크로에서 구조 유형을 얻는 더 깨끗한 방법이 있습니까?
답변
이 질문은 여기 Travis에 의해 두 번 답변 됩니다 . 트래커와 유진의 토론 (댓글 및 메일 링리스트)에 문제에 대한 링크가 있습니다.
유형 검사기의 유명한 “Skylla and Charybdis”섹션에서, 우리의 영웅은 어두운 익명 성을 피하고 구조적 유형의 구성원으로 빛을 볼 것을 결정합니다.
타입 체커를 속이는 몇 가지 방법이 있습니다 (Odysseus의 양 포옹 계획은 포함되지 않습니다). 가장 간단한 방법은 블록이 익명 클래스처럼 보이지 않고 그 뒤에 인스턴스화가 발생하도록 더미 명령문을 삽입하는 것입니다.
입력자가 외부에서 참조하지 않는 공개 용어임을 알게되면 귀하를 비공개로 만들 것입니다.
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)
}
}