2016-12-11 6 views
1

ケースクラス値を受け取り、指定された位置でケースクラスパラメータの値を返す関数に対してTreeを生成しようとしています。これはプライベートパラメータの値を抽出するのに便利です。文字列を補間する前にクォーコットに挿入する

import reflect.runtime.currentMirror 
    import scala.reflect.runtime.universe._ 
    import scala.tools.reflect.ToolBox 
    val tb = currentMirror.mkToolBox() 

    case class A(private val first: Int) 

    case class B(first: Int, private val second: Int) 

    def get(tpe: Type, position: Option[Int]): Tree = { 
    val pos = s"${position.map(p => s"._${p + 1}").getOrElse("")}" 
    tb.parse(s"(a: $tpe) => $tpe.unapply(a).get$pos") 
    } 

    println(tb.eval(get(typeOf[A], None)).asInstanceOf[(A) => Int](A(1))) 

    println(tb.eval(get(typeOf[B], Some(1))).asInstanceOf[(B) => Int](B(1, 2))) 

はまた、私は次の依存関係を追加しました:ケースクラスはパラメータを1つしか持っていたときに

scalaVersion := "2.11.8" 
libraryDependencies ++= Seq(
    "org.scala-lang" % "scala-reflect" % scalaVersion.value, 
    "org.scala-lang" % "scala-compiler" % scalaVersion.value 
) 

positionNoneです。

私の解決策は動作していますが、どうすればtb.parse(s"...")をquasiquoteq"..."に置き換えることができますか?

私はそれを試してみましたが、それはで失敗します。私は、実行時に構築されていますし、q"..."tb.parseとは異なり、コンパイル時に解析される準クォートいくつかの文字列に挿入することはできません理解できるように

Don't know how to unquote here 
[error]  q"(a: $tpe) => $tpe.unapply(a).get$pos" 
[error]          ^

私はそうですか?

また、このような補間は安全ですかs"(a: $tpe) => $tpe.unapply(a).get$pos"q"..."構文を使用するとき、quasiquoteは$tpeTypeであることを知っていますが、文字列補間は文字列を作成します。私はこれが常により複雑で具体的な場合にはうまくいくとは確信していません。

答えて

1

あなたはではありません。です。 Quasiquotesは通常の文字列を補間するだけではなく、他のASTを補間する必要があります。 ASTの一部を自分で構築する場合は、qと一緒に使用できます。したがって、あなたはこれを書くことができます:

def getElem(tpe: Type, pos: Option[Int]): Tree = { 
    // Make something like TermName("_N") 
    val tupleAccess = pos.map("_" + _).map(TermName.apply) 
    val tupleExpr = { 
    val plain = q"${tpe.typeSymbol.companion}.unapply(a).get" 
    // Build into $companion.unapply(a).get(._N) 
    tupleAccess.foldLeft(plain)(Select.apply) 
    } 
    q"(a: $tpe) => $tupleExpr" 
} 

そしてvoilà!

object A { object B { object C { case class D(private val x: Int, val y: String) } } } 
val all = tb.eval(getElem(typeOf[A.B.C.D], None)).asInstanceOf[A.B.C.D => (Int, String)] 
val int = tb.eval(getElem(typeOf[A.B.C.D], Some(1))).asInstanceOf[A.B.C.D => Int] 
val str = tb.eval(getElem(typeOf[A.B.C.D], Some(2))).asInstanceOf[A.B.C.D => String] 
val ds = { 
    val D = A.B.C.D 
    List(D(1, "one"), D(2, "two"), D(3, "three")) 
} 
ds.map(all) == List((1, "one"), (2, "two"), (3, "three")) 
ds.map(int) == List(1, 2, 3) 
ds.map(str) == List("one", "two", "three") 
+0

私は「選択」を忘れました。それに関する詳細な文書はありますか? – mixel

+0

http://scala-lang.org/api/current/scala-reflect 'Select'は' x.y'の '.'です。 – HTNW

関連する問題