构造函数与方法签名

时间:2017-12-07 19:57:23

标签: scala type-inference generic-programming implicits

编译器在BroFinder1BroFinder2中看到哪些导致第一个失败的区别?我确实需要BroFinder1才能正常工作,而不使用Aux Pattern等模式。

trait Brother[I] {
  type Bro
  def get: Bro
}

class Foo
object Foo {
  implicit object bro extends Brother[Foo] {
    override type Bro = Bar

    override def get = new Bar
  }
}

class Bar {
  def barSpecificAction(): Unit = ()
}

class BroFinder1[I](implicit val b: Brother[I]) {
  def brotherOf: b.Bro = b.get
}

class BroFinder2[I] {
  def brotherOf(implicit b: Brother[I]): b.Bro = b.get
}


new BroFinder1[Foo].brotherOf.barSpecificAction() // Doesn't compile
//error: Error:(73, 32) value barSpecificAction is not a member of _1.b.Bro
//new BroFinder1[Foo].brotherOf.barSpecificAction();
                          ^
new BroFinder2[Foo].brotherOf.barSpecificAction() // Ok

//scala version: 2.12.4

2 个答案:

答案 0 :(得分:3)

这不是一个完美的答案,但可能会提供一些见解。该问题似乎与implicit无关。它似乎与另一个相当先进的Scala功能有关: 路径依赖类型 。特别是麻烦似乎来自这样一个事实:Scala类型系统不够强大,无法在以下代码中精确表达finder1finder2之间的类型差异:

object Foo {

  implicit object bro extends Brother[Foo] {
    override type Bro = Bar

    override def get = new Bar
  }

  object bro2 extends Brother[Foo] {
    override type Bro = Foo

    override def get = new Foo
  }

}


val finder1 = new BroFinder1[Foo]
val finder2 = new BroFinder1[Foo]()(Foo.bro2)
val bof1 = finder1.brotherOf
val bof2 = finder2.brotherOf

Sidenote :某些参数为implicit的事实并不会使其成为“常量”,因为您始终可以显式传递参数或者您在不同的上下文中可以有不同的可见隐含值。

此示例中可能分配finder1finder2的最精确类型的AFAIU仅为BroFinder1[Foo]。特别是没有办法捕获b隐式变量的不同值,因此无法进一步准确传递在该值内编码的路径相关类型的值。因此,最好的编译器知道bof1bof2的类型,它们都具有_1.b.Bro形式的类型,其中_1表示BroFinder1[Foo]的某个特定实例。因此,编译器无法确定bof1实际上是Bar类型,而bof2类型为Foo

第二个示例有效,因为隐式参数是在同一个上下文中捕获的,因此编译器确切知道brotherOf的结果类型是什么。

我知道的唯一解决方法并不完全适用于您的情况:使用“ Aux ”类型。如果您的BroFinder也采用类型为Foo的显式参数,则解决方案可能如下所示:

type BrotherAux[I, B] = Brother[I] {type Bro = B}

class BroFinder3[I, B](val v: I)(implicit val b: BrotherAux[I, B]) {
  def brotherOf: B = b.get
}

new BroFinder3(new Foo).brotherOf.barSpecificAction()

但在你的情况下,它必须没那么有用

class BroFinder1[I, B](implicit val b: BrotherAux[I, B]) {
  def brotherOf: B = b.get
}

new BroFinder1[Foo, Bar].brotherOf.barSpecificAction()

答案 1 :(得分:3)

因为new BroFinder2[Foo].brotherOf的类型为BarBar的方法为barSpecificAction,但new BroFinder1[Foo].brotherOf属于存在类型

x.Bro forSome { val x: Brother[Foo] }

并且它没有方法barSpecificAction