복붙노트

[SCALA] 매크로에서 익명 클래스의 방법과 구조 유형을 얻기

SCALA

매크로에서 익명 클래스의 방법과 구조 유형을 얻기

우리는 어떤 종류의 회원 또는 방법으로 익명의 클래스를 정의하고 정적 등 그 방법과 구조 유형이 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 내 생성자 방법을 제공하는 편리한 특성입니다.)

이 매크로는 우리가 문자열 리터럴로 익명 클래스의 타입 멤버의 이름을 지정할 수 있습니다 :

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

이것은 매우 편리합니다 - 그것은 당신을 위해이 같은 일을 예를-하지만 작동 왜 이해가 안 할 수 있으며, 형식 멤버 버전 작동하지만 바 없습니다. 나는이 알고 동작을 정의 할 수 없습니다 수 있지만, 그것은 어떤 의미가 있습니까? 매크로에서 (그것의 방법과) 구조 유형을 얻을 수있는 청소기 방법이 있나요?

해결법

  1. ==============================

    1.이 질문은 여기 트래비스에 의해 이중으로 대답한다. (코멘트와 메일 링리스트) 추적기와 유진의 토론에 대한 문제에 대한 링크가 있습니다.

    이 질문은 여기 트래비스에 의해 이중으로 대답한다. (코멘트와 메일 링리스트) 추적기와 유진의 토론에 대한 문제에 대한 링크가 있습니다.

    유형 검사의 유명한 "스킬라와 디즈"섹션에서, 우리의 영웅은 어두운 익명 성을 탈출 및 구조 유형의 구성원으로 빛을 보게 될 것을 결정한다.

    (양 포옹의 오디세우스의 계략을 수반하지 않는) 유형 검사를 속일 수있는 방법이 몇 가지 있습니다. 간단한 익명 클래스가 인스턴스화 다음과 같은 블록이 보이지 않도록 더미 문을 삽입하는 것입니다.

    당신이 외부에서 참조되지 않는 공공 용어있어하는 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)
      }
    }
    
  2. from https://stackoverflow.com/questions/14370842/getting-a-structural-type-with-an-anonymous-classs-methods-from-a-macro by cc-by-sa and MIT license