Suppose I have some nested traits:
trait Foo { trait Bar }
And a couple of instances:
val myFoo = new Foo {}
val myBar = new myFoo.Bar {}
I can write the following, which look (at least at a glance) like they should do more or less the same thing:
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 }
Note that the last is inspired by the solution given here.
The first three work:
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
But not the fourth or fifth:
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)
^
Fair enough—we can't do the following, either, probably for similar reasons:
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 {}
^
And it's not a big deal, anyway, since we've got three working solutions.
(I'll start by noting that while I first ran into the problem below while working with macros, and while my example here involves the reflection API, my question is not specific to macros or reflection.)
Suppose that I'm working with the new reflection API and want to be able to write the following:
applier[List[_]](Literal(Constant(42)), Literal(Constant(13)))
And have it mean something like "give me the abstract syntax tree for List(42, 13)
". This isn't too hard—I can just use the approach from whatever3
above:
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
)
}
}
And now I get the syntax I want in my macros (see my answer to this question for a more detailed and motivated example):
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))
}
}
And everything works as intended. I don't really like the "reflective access of structural type member" business, though. Unfortunately I can't use the whatever1
or whatever2
approaches here, since I can't fix the type parameter when I apply this thing to my universe. I'd love to be able to write the following:
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
)
}
But this of course runs me into the type mismatch problems we saw with whatever4
above.
Is there some other trick I'm missing? Is it possible for me to get the syntax I want without using an anonymous class with a structural type member?
This should work:
case class companionApplier[U <: Universe](u: U) { ... }
// in macro
companionApplier[c.universe.type](c.universe)
I had a similar question some months ago, see here.
How about splitting the structural type out as an auxiliary type and then sprinkling a little of my solution from the list,
scala> trait Foo { trait Bar }
defined trait Foo
scala> val myFoo = new Foo {} ; val myBar = new myFoo.Bar {}
myFoo: Foo = $anon$1@11247416
myBar: myFoo.Bar = $anon$2@70415924
scala> class Whatever6Aux[F <: Foo](val foo: F) { def apply(bar: foo.Bar) = bar }
defined class Whatever6Aux
scala> def whatever6(foo: Foo) = new Whatever6Aux[foo.type](foo)
whatever6: (foo: Foo)Whatever6Aux[foo.type]
scala> import scala.language.existentials
import scala.language.existentials
scala> whatever6(myFoo)(myBar)
res0: _1.foo.Bar forSome { val _1: Whatever6Aux[<refinement>.type] } = $anon$2@70415924
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With