バックグラウンド
ネストされた特性がいくつかあるとします。
trait Foo { trait Bar }
そしていくつかの例:
val myFoo = new Foo {}
val myBar = new myFoo.Bar {}
次のように書くことができますが、(少なくとも一見すると) 多かれ少なかれ同じことを行う必要があるように見えます。
def whatever1(foo: Foo)(bar: foo.Bar) = bar
def whatever2(foo: Foo): foo.Bar => foo.Bar = { bar => bar }
def whatever3(foo: Foo) = new { def apply(bar: foo.Bar) = bar }
case class whatever4(foo: Foo) { def apply(bar: foo.Bar) = bar }
case class whatever5[F <: Foo](foo: F) { def apply(bar: foo.Bar) = bar }
最後はここで与えられた解決策に触発されていることに注意してください。
最初の 3 つの作業:
scala> val sameBar1: myFoo.Bar = whatever1(myFoo)(myBar)
sameBar1: myFoo.Bar = $anon$1@522f63e7
scala> val sameBar2: myFoo.Bar = whatever2(myFoo)(myBar)
sameBar1: myFoo.Bar = $anon$1@522f63e7
scala> val sameBar3: myFoo.Bar = whatever3(myFoo)(myBar)
sameBar2: myFoo.Bar = $anon$1@522f63e7
しかし、4 番目または 5 番目ではありません。
scala> val sameBar4: myFoo.Bar = whatever4(myFoo)(myBar)
<console>:12: error: type mismatch;
found : myFoo.Bar
required: _1.foo.Bar where val _1: whatever4
val sameBar4: myFoo.Bar = whatever4(myFoo)(myBar)
^
おそらく同様の理由で、次のこともできません。
scala> val myOof = myFoo
myOof: Foo = $anon$1@39e4ff0c
scala> val myOofBar: myOof.Bar = new myFoo.Bar {}
<console>:10: error: type mismatch;
found : myFoo.Bar
required: myOof.Bar
val myOofBar: myOof.Bar = new myFoo.Bar {}
^
とにかく、3 つの実用的なソリューションがあるので、大したことではありません。
問題
(マクロを操作しているときに最初に以下の問題に遭遇しましたが、ここでの例にはリフレクション API が含まれていますが、私の質問はマクロやリフレクションに固有のものではないことに注意してください。)
新しいリフレクション API を使用していて、次のように記述できるようにしたいとします。
applier[List[_]](Literal(Constant(42)), Literal(Constant(13)))
そして、「の抽象構文木をください」のような意味にしますList(42, 13)
。これはそれほど難しくありませんwhatever3
。上記のアプローチをそのまま使用できます。
trait ReflectionUtils {
import scala.reflect.api.Universe
def companionApplier(u: Universe) = new {
def apply[A: u.TypeTag](xs: u.Tree*): u.Tree = u.Apply(
u.Select(u.Ident(u.typeOf[A].typeSymbol.companionSymbol), "apply"),
xs.toList
)
}
}
そして今、マクロで必要な構文を取得しました (より詳細で動機付けされた例については、この質問に対する私の回答を参照してください)。
object MacroExample extends ReflectionUtils {
import scala.language.experimental.macros
import scala.language.reflectiveCalls
import scala.reflect.macros.Context
def threeOfThem(n: Int) = macro threeOfThem_impl
def threeOfThem_impl(c: Context)(n: c.Expr[Int]) = {
val applier = companionApplier(c.universe)
c.Expr[List[Int]](applier[List[_]](n.tree, n.tree, n.tree))
}
}
そして、すべてが意図したとおりに機能します。ただ、「構造型メンバーのリフレクション アクセス」ビジネスはあまり好きではありません。残念ながら、ここではwhatever1
orwhatever2
アプローチを使用できません。これをユニバースに適用するときに型パラメーターを修正できないためです。以下のように書いていただけると幸いです。
case class companionApplier(u: Universe) {
def apply[A: u.TypeTag](xs: u.Tree*): u.Tree = u.Apply(
u.Select(u.Ident(u.typeOf[A].typeSymbol.companionSymbol), "apply"),
xs.toList
)
}
しかしもちろん、これは上記で見た型の不一致の問題に突き当たりますwhatever4
。
私が見逃している他のトリックはありますか?構造型メンバーを持つ匿名クラスを使用せずに、必要な構文を取得することは可能ですか?