Типы, зависящие от пути и вложенные черты

Фон

Предположим, что у меня есть некоторые вложенные черты:

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 = [email protected]

scala> val sameBar2: myFoo.Bar = whatever2(myFoo)(myBar)
sameBar1: myFoo.Bar = [email protected]

scala> val sameBar3: myFoo.Bar = whatever3(myFoo)(myBar)
sameBar2: myFoo.Bar = [email protected]

Но не четвертый или пятый:

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 = [email protected]

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
    )
  }
}

И теперь я получаю синтаксис, который я хочу в своих макросах (см. answer to this вопрос для более подробного и мотивированного примера):

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 выше.

Есть ли какой-то другой трюк, который мне не хватает? Возможно ли получить синтаксис, который я хочу, без использования анонимного класса со структурным членом?

Ответ 1

Это должно работать:

case class companionApplier[U <: Universe](u: U) { ... }

// in macro
companionApplier[c.universe.type](c.universe)

У меня был аналогичный вопрос несколько месяцев назад, см. здесь.

Ответ 2

Как насчет разделения структурного типа как вспомогательного типа, а затем покроя немного моего решения из списка,

scala> trait Foo { trait Bar }
defined trait Foo

scala> val myFoo = new Foo {} ; val myBar = new myFoo.Bar {}
myFoo: Foo = [email protected]
myBar: myFoo.Bar = [email protected]

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] } = [email protected]