【问题标题】:Puzzled by type mismatch when applying a Scala Function1应用 Scala 函数时对类型不匹配感到困惑1
【发布时间】:2014-12-15 03:11:41
【问题描述】:

以下内容无法使用 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

注释掉不进行类型检查的变体,我们得到:

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(无 Function1 类型别名) compute3a compute5

对于 Function1 类型的别名,我们只剩下 2 个备选方案:compute3acompute5

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

【问题讨论】:

    标签: scala type-mismatch


    【解决方案1】:

    这是因为路径依赖类型在 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)
    

    更新 但是对于compute1来说仍然应该是这种情况

    【讨论】:

    • 感谢@Eugene 的解释(依赖于路径的类型)。这种方法似乎不切实际,因为我必须将 T 的实例作为不能隐含的参数传递。
    【解决方案2】:

    @Eugene 认为这是由路径相关类型引起的,这是正确的 - 您可以使用以下方法修复它

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

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

    【讨论】:

    • 另一种选择是在 Util 中定义 F
    • 感谢@Zim-Zam 的建议。然而,这种方法在定义 trait Test 的特化时会导致问题,因为函数参数的类型在 Scala 中是相反的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多