背景
假设我有一些嵌套的特征:
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 }
请注意,最后一个是受到此处给出的解决方案的启发。
前三项工作:
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
但不是第四个或第五个:
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 {}
^
无论如何,这没什么大不了的,因为我们有三个可行的解决方案。
问题
(我首先要注意,虽然我在使用宏时第一次遇到下面的问题,虽然我的示例涉及反射 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
上面看到的类型不匹配问题。
我还缺少其他技巧吗?我是否可以在不使用具有结构类型成员的匿名类的情况下获得我想要的语法?