2014-12-06 4 views
1

필드의 경우 getter/setter 쌍을 반환하는 스칼라 매크로 &을 만들고 싶습니다. 메소드의 경우 부분적으로 적용된 함수 인 경우도 있습니다. 다음과 같은 뭔가 :필드 및 메소드 포인터를 만드는 스칼라 매크로

trait ValRef[T] { 
    def get(): T 
} 

trait VarRef[T] extends ValRef[T] { 
    def set(x: T): Unit 
} 

// Example: 

case class Foo() { 
    val v = "whatever" 
    var u = 100 
    def m(x: Int): Unit 
} 

val x = new Foo 
val cp: ValRef[String] = &x.v 
val p: VarRef[Int] = &x.u 
p.set(300) 
val f: Int => Unit = &x.m 
f(p.get()) 

그러나 나는 내가 할 사람들을 위해 매우 간단합니다 가정 스칼라 매크로 경험이 없다.

+2

그것은 성명이었고 질문이 아니 었습니다. 가능한지 여부는 질문 이었습니까? – johanandren

+0

비슷한 코드 또는 스케치가 제공됩니다. 고마워. –

답변

3

최근 스칼라의 매크로에 대한 읽기를 시작하여 흥미로운 연습 문제를 발견했습니다. valvar 값에 대한 포인터 만 구현했지만 코드의 스케치를 요구하기 때문에 지금까지 찾은 것을 공유 할 수 있다고 생각했습니다.

편집 : : 메소드에 대한 포인터 : 뭔가 오해하지 않는다면 이미 스칼라의 기능입니다. 당신은 코드에만 관심이 있다면 class Foo{ def m(i: Int): Unit = println(i)} 주어, 당신은 val f: Int => Unit = new Foo().m _

  • 같은 기능을 얻을 대답의 하단으로 스크롤합니다.

매크로는 컴파일 할 때 실행되므로 미리 컴파일해야합니다. IntelliJ (또는 Eclipse)를 사용하는 경우 모든 매크로 관련 코드를 별도의 프로젝트에 넣는 것이 좋습니다.

당신이 언급 한 바와 같이

, 우리는 두 개의 포인터 특성이

trait ValRef[T] { 
    def get(): T 
} 

trait VarRef[T] extends ValRef[T] { 
    def set(x: T): Unit 
} 

이제 우리는 주어진 참조, 즉, name 또는 qualifier.name이하는 ValRef 반환하는 방법 &을 구현하고자합니다. 참조가 변경 가능한 값을 참조하면 결과는 VarRef이어야합니다.

def &[T](value: T): ValRef[T] 

지금까지는 일반적인 스칼라 코드입니다. 메서드 &은 모든 식을 받아들이고 인수와 같은 형식의 ValRef을 반환합니다.

def pointer[T: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)(value: c.Expr[T]) = ??? 

이 서명은 대부분 표준으로한다 :

우리가 &의 논리를 구현하는 매크로를 정의 할 수 있습니다 - Context -

  • c은을 사용하는 컴파일러에 의해 수집 된 정보를 포함 매크로.
  • T
  • &
  • value&의 인수에 해당 전달되는 식의 형태이지만, 구현은 스칼라의 AST에서 작동하기 때문에, 그것은 유형 Expr[T]이며 원본이 아닌 유형의 T

약간 특별한 것은 WeakTypeTag의 사용입니다. 나는 완전히 자신을 이해하지 못합니다.documentation 상태 :

유형 매개 변수는 WeakTypeTag 컨텍스트 경계와 함께 제공 될 수 있습니다. 이 경우 응용 프로그램 사이트에서 인스턴스화 된 실제 유형 인수를 설명하는 해당 유형 태그는 매크로가 확장 될 때 함께 전달됩니다.

삽입 부분은 pointer의 구현입니다. 메소드 &이 호출 될 때마다 메소드의 결과는 컴파일러에 의해 사용되기 때문에, AST을 리턴해야합니다. 그러므로 우리는 나무를 만들고 싶습니다. 문제는 트리가 어떻게 생겼을 까?

Scala 2.11 이후 Quasiquotes이라는 것이 있습니다. Quasiquotes는 문자열 값에서 트리를 만드는 데 도움이 될 수 있습니다.

우선 문제를 단순화합시다. valvar 개의 참조를 구분하는 대신 항상 VarRef을 반환합니다. 반환해야 x.y

  • get()에 의해 생성 된 VarRef를 들어 x.y
  • set(x)

x.y = x 그래서 우리는 VarRef[T]의 익명 서브 클래스의 인스턴스를 나타내는 트리를 생성 할 실행해야합니다. 다음과 같이 우리가 Quasiquote에 직접 제네릭 형식 T를 사용할 수 없기 때문에, 우리가 처음 우리가 지금

val tpe = value.tree.tpe에 의해 얻을 수있는 유형의 트리 표현이 필요, 우리의 Quasiquote 보인다 :

q""" 
    new VarRef[$tpe] { 
    def get(): $tpe = $value 

    def set(x: $tpe): Unit = { 
     $value = x 
    } 
    } 
""" 

이 구현 var 참조에 대한 포인터 만 작성하는 한 계속 작업해야합니다. 그러나 val 참조에 대한 포인터를 만들 자마자 "val에 대한 재 할당"때문에 컴파일이 실패합니다. 따라서 우리 매크로는이 둘을 구별해야합니다.

분명히 Symbols은 이러한 종류의 정보를 제공합니다. 포인터는 참고 용으로 만 만들어지며, 이는 TermSymbol을 제공해야합니다.

val symbol: TermSymbol = value.tree.symbol.asTerm 

는 이제 TermSymbol API는 방법 isValisVar으로 우리를 제공하지만, 그들은 로컬 변수 일 것으로 보인다. 나는 "올바른 방법"참조가, var 또는 val가 있는지 여부를 발견 모르겠어요하지만 다음은 작동하는 것 같다 :

if(symbol.isVar || symbol.setter != NoSymbol) { 

자격을 갖춘 이름의 상징이 setter 기호를 제공하는 것이다 트릭을 iffvar입니다. 그렇지 않은 경우 setterNoSymbol을 반환합니다.

case class Foo() { 
    val v = "whatever" 
    var u = 100 
} 

object Example{ 
    import PointerMacro.& 

    def main(args: Array[String]): Unit = { 
    val x = new Foo 
    val mainInt = 90 
    var mainString = "this is main" 

    val localValPointer: ValRef[Int] = &(mainInt) 
    val localVarPointer: VarRef[String] = &(mainString).asInstanceOf[VarRef[String]] 
    val memberValPointer: ValRef[String] = &(x.v) 
    val memberVarPointer: VarRef[Int] = &(x.u).asInstanceOf[VarRef[Int]] 

    println(localValPointer.get()) 
    println(localVarPointer.get()) 
    println(memberValPointer.get()) 
    println(memberVarPointer.get()) 

    localVarPointer.set("Hello World") 
    println(localVarPointer.get()) 

    memberVarPointer.set(62) 
    println(memberVarPointer.get()) 

    } 
} 
:이 코드를 컴파일하고 프로젝트의 클래스 패스에 추가하는 경우

trait ValRef[T] { 
    def get(): T 
} 

trait VarRef[T] extends ValRef[T] { 
    def set(x: T): Unit 
} 

object PointerMacro { 

    import scala.language.experimental.macros 

    def pointer[T: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)(value: c.Expr[T]) = { 
    import c.universe._ 

    val symbol: TermSymbol = value.tree.symbol.asTerm 
    val tpe = value.tree.tpe 

    if(symbol.isVar || symbol.setter != NoSymbol) { 
     q""" 
     new VarRef[$tpe] { 
      def get(): $tpe = $value 

      def set(x: $tpe): Unit = { 
      $value = x 
      } 
     } 
     """ 
    } else { 
     q""" 
     new ValRef[$tpe] { 
      def get(): $tpe = $value 
     } 
     """ 
    } 
    } 

    def &[T](value: T): ValRef[T] = macro pointer[T] 
} 

는, 당신은 다음과 같이 포인터를 만들 수 있어야 다음과 같이


그래서 매크로 코드가 보인다

하는 실행할 때, 인쇄해야

90 
this is main 
whatever 
100 
Hello World 
62