简体   繁体   English

路径依赖类型和嵌套特征

[英]Path-dependent types and nested traits

Background 背景

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. 无论如何,这不是什么大问题,因为我们有三个工作解决方案。

The problem 问题

(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.) (我首先要注意的是,虽然我在使用宏时遇到了下面的问题,而我的示例涉及反射API,但我的问题不是特定于宏或反射。)

Suppose that I'm working with the new reflection API and want to be able to write the following: 假设我正在使用新的反射API并希望能够编写以下内容:

applier[List[_]](Literal(Constant(42)), Literal(Constant(13)))

And have it mean something like "give me the abstract syntax tree for List(42, 13) ". 并且它意味着“给我List(42, 13)的抽象语法树”。 This isn't too hard—I can just use the approach from whatever3 above: 这不是太难 - 我可以使用上述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
    )
  }
}

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. 不幸的是我不能在这里使用whatever1whatever2方法,因为当我将这个东西应用到我的宇宙时我无法修复类型参数。 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. 但这当然让我陷入了我们用上述whatever4看到的类型不匹配问题。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM