路径依赖类型和嵌套特征

时间:2013-03-19 20:52:17

标签: scala types nested-class path-dependent-type dependent-method-type

背景

假设我有一些嵌套特征:

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 }

请注意,最后一个受到here给出的解决方案的启发。

前三项工作:

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

现在我在我的宏中获得了我想要的语法(请参阅我的answerthis question以获取更详细和更有动力的示例):

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

一切都按预期工作。不过,我并不喜欢“结构型会员的反思性访问”业务。不幸的是,我不能在这里使用whatever1whatever2方法,因为当我将这个东西应用到我的宇宙时,我无法修复类型参数。我很乐意写下以下内容:

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 个答案:

答案 0 :(得分:3)

这应该有效:

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

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

几个月前我遇到过类似的问题,请参阅here

答案 1 :(得分:3)

如何将结构类型拆分为辅助类型,然后从列表中提取一些我的解决方案,

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