繁体   English   中英

应用Scala Function1时,类型不匹配引起了困惑

[英]Puzzled by type mismatch when applying a Scala Function1

以下内容无法使用Scala 2.11.4进行编译:

trait Test {
  type A
  type F = Function1[A, String]
}

trait Util[T <: Test] {
  def compute1( f: T#A => String, a: T#A ): String = f(a)
  def compute2( f: T#F, a: T#A ): String = f(a)
  //                                         ^
}

调用(^)的参数上存在编译错误:

type mismatch; 
found : a.type (with underlying type T#A) required: _3317.A

我期望compute1和compute2中的两个f参数具有相同的类型; 显然不是。

这是怎么回事?

正如@Eugene和@ Zim-Zam回答的那样,这里的问题归因于Scala的路径相关类型。 根据他们的建议,我提出了几种选择:

trait Test {
  type A
  type Fa = Function1[A, String]       // original question
  type Fb = Function1[Test#A, String]  // Zim-Zam's suggestion
}

trait TestOps[T <: Test] {
  type G = Function1[T#A, String]
}

trait Util[T <: Test] {
  def compute1( f: T#A => String, a: T#A ): String = f(a)
  // def compute2a( f: T#Fa, a: T#A ): String = f(a)
  // type mismatch; found : a.type (with underlying type T#A) required: _1536.A
  def compute2b( f: T#Fb, a: T#A ): String = f(a)
}

trait Util1 {  
  def compute3a(t: Test)( f: t.Fa, a: t.A ): String = f(a)
  def compute3b(t: Test)( f: t.Fb, a: t.A ): String = f(a)
}

trait Util2[T <: Test] { tops: TestOps[T] =>
  // def compute4a( f: T#Fa, a: T#A ): String = f(a)
  // type mismatch; found : a.type (with underlying type T#A) required: _1642.A
  def compute4b( f: T#Fb, a: T#A ): String = f(a)
  def compute5( f: tops.G, a: T#A ): String = f(a)
}

Util将原始操作与@ Zim-Zam的建议进行了比较

Util1在指定Function1参数类型上有区别: A vs Test#A

Util2行使建议将Function1类型定义为另一个特征:TestOps

注释掉不键入check的变体,剩下的就是:

compute1 compute2b compute3a compute3b compute4b compute5

哪种方法更适合这些特征?

为了弄清差异,我做了一个简单的改进:

class U 

class TestU extends Test {
  override type A = U  
}

class UOps extends TestOps[TestU]

结果如下:

trait UtilU extends Util[TestU] {
  def get1( f: TestU#A => String, a: TestU#A) = compute1(f, a)
  // def get2b( f: TestU#A => String, a: TestU#A) = compute2b(f, a)
  // type mismatch; found : A.U ⇒ String required: A.Test#A ⇒ String
}

trait UtilU1 extends Util1 {
  val u = new TestU()
  def get3a( f: u.A => String, a: u.A) = compute3a(u)(f, a)
  // def get3b( f: u.A => String, a: u.A) = compute3b(u)(f, a)
  // type mismatch; 
  // found : UtilU1.this.u.A ⇒ String (which expands to) A.U ⇒ String 
  // required: UtilU1.this.u.Fb (which expands to) A.Test#A ⇒ String
}

class UtilU2 extends Util2[TestU] with TestOps[TestU] {
  // def get4b( f: TestU#A => String, a: TestU#A) = compute4b(f, a)
  // type mismatch; found : A.U ⇒ String required: A.Test#A ⇒ String
  def get5( f: TestU#A => String, a: TestU#A) = compute5(f, a)
}

因此,我们只剩下3个合理的变化:

compute1 (无功能1类型别名) compute3a compute5

对于别名为Function1的类型,我们只剩下2种选择: compute3acompute5

描述它们之间差异的正确方法是什么?

这是因为在scala中如何实现依赖路径的类型

val test1 = new Test {}
val test2 = new Test {}
// test1.A is different type from test2.A

在您的示例中,您基本上是说f和a可以从Test的不同实例传递,并且在这种情况下,第一个参数的类型F中使用的A将不同于第二个参数的A。

但是,如果将其限制为T的某个实例,它将进行编译

def compute2(t: T)( f: t.F, a: t.A ): String = f(a)

Upd然而,compute1仍然应该如此

@Eugene关于路径相关类型导致的错误是正确的-您可以使用以下方式修复它

trait Test {
  type A
  type F = Function1[Test#A, String]
}

现在F可以将任何A作为参数

暂无
暂无

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

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