Зависимые от пути типы и вложенные признаки
Фон
Предположим, что у меня есть некоторые вложенные черты:
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 reflection и хочу иметь возможность написать следующее:
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
или whatever2
здесь, поскольку я не могу исправить параметр типа, когда я применяю эту вещь к моей вселенной. Я хотел бы иметь возможность написать следующее:
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
выше.
Есть ли еще какой-нибудь трюк, который я упускаю? Возможно ли получить нужный синтаксис без использования анонимного класса с членом структурного типа?2 ответа:
Это должно сработать:
case class companionApplier[U <: Universe](u: U) { ... } // in macro companionApplier[c.universe.type](c.universe)
У меня был похожий вопрос несколько месяцев назад, см. здесь.
Как насчет того, чтобы разделить структурный тип как вспомогательный тип, а затем разбрызгать немного моего решения из списка,
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