在带有 DOT 演算的 Scala 3 中,`this.type` 是路径依赖类型吗?它有什么特别之处?

问题描述 投票:0回答:2

本题来源于:

Scala:抽象类型与泛型

在 Scala 3 中,路径依赖类型是一种类型/绑定,它使用不同的编译时路径签名绑定术语/对象。因此,一旦为特征定义(其上限 == 下限),它就被认为是最终的并且不能在任何实现对象中被覆盖:

  object Case1 {

    trait Sub {
      type EE
    }

    trait S1 extends Sub { type EE = Product }
    trait S2 extends Sub { type EE = Tuple }

    trait A1 extends S1

    trait A2 extends A1 with S2
  }

---

: error overriding type EE in trait S1, which equals Product;
  type EE in trait S2, which equals Tuple trait A2 inherits conflicting members:
  type EE in trait S1, which equals Product  and
  type EE in trait S2, which equals Tuple
(Note: this can be resolved by declaring an override in trait A2.)

但是这个规则有一个例外:

this.type
可以轻松绕过它:

  object Case2 {

    trait Supe {

      type E

      trait Sub {

        type EE = Supe.this.E
      }
    }

    object S1 extends Supe {
      type E = Product
    }
    object S2 extends Supe {
      type E = Tuple
    }

    trait A1 extends S1.Sub

    trait A2 extends A1 with S2.Sub
  }

// No compilation error

为什么它作为路径依赖类型很特别?如果不是,那是什么?我找不到 DOT 演算的任何部分为其提出排他性规则。

scala dependent-type path-dependent-type dotty
2个回答
2
投票

type EE = Supe.this.E
只是
type EE = E
.

Case 1和Case 2没有太大区别,都是非法继承。在 Scala 2 和 Scala 3 中都是非法的。Scala 2 和 Scala 3 之间的区别就在于出现错误的时间。在 Scala 2 中,你会立即得到一个错误,在 Scala 3 中,当你用一个类实现一个特征时,你会稍后得到一个错误。

// Scala 2

object Case1 {
  trait Sub {
    type EE
  }

  trait S1 extends Sub {
    type EE = Product
  }

  trait S2 extends Sub {
    type EE = Tuple
  }

  trait A1 extends S1
  trait A2 extends A1 with S2 //trait A2 inherits conflicting members: type EE = Product (defined in trait S1) and type EE = Tuple (defined in trait S2)
}

object Case2 {
  trait Supe {
    type E

    trait Sub {
      type EE = Supe.this.E
    }
  }

  object S1 extends Supe {
    type E = Product
  }

  object S2 extends Supe {
    type E = Tuple
  }

  trait A1 extends S1.Sub
  trait A2 extends A1 with S2.Sub //illegal inheritance; trait A2 inherits different type instances of trait Sub: Case2.S2.Sub and Case2.S1.Sub
}
// Scala 3

object Case1 {
  trait Sub {
    type EE
  }

  trait S1 extends Sub {
    type EE = Product
  }

  trait S2 extends Sub {
    type EE = Tuple
  }

  trait A1 extends S1
  trait A2 extends A1 with S2 //error overriding type EE in trait S1, which equals Product
}

object Case2 {
  trait Supe {
    type E

    trait Sub {
      type EE = Supe.this.E
    }
  }

  object S1 extends Supe {
    type E = Product
  }

  object S2 extends Supe {
    type E = Tuple
  }

  trait A1 extends S1.Sub
  trait A2 extends A1 with S2.Sub

  // added
  class A3 extends A2 // class A3 cannot be instantiated since it has conflicting base types Case2.S1.Sub and Case2.S2.Sub
  val a2 = new A2 {} // anonymous class Object with Case2.A2 {...} cannot be instantiated since it has conflicting base types Case2.S1.Sub and Case2.S2.Sub
}

我想我已经看到了在错误跟踪器上讨论的 Scala 3 的这些细节。如果我找到链接,我会添加它。


0
投票

在 Scala 中,this.type 是一种特殊类型,表示定义方法或字段的对象的类型。它被称为单例类型,因为它只能引用单个对象。

在路径依赖类型的上下文中,this.type 被视为一种特殊情况,因为它不像其他路径依赖类型那样绑定到不同的编译时路径签名。相反,它指的是定义它的确切对象。因此,它可以在任何实现对象中被覆盖,而不会与其他路径相关类型发生冲突。

在给定的示例中,路径依赖类型 Supe.this.E 指的是在 Supe 特征中定义的 E 类型,然后将其用作 Sub 特征中的 EE 类型。由于 Supe.this.E 不是一个独特的编译时路径签名,它可以在任何实现对象中被覆盖,因此代码不会产生任何编译错误。

总而言之,this.type 是路径相关类型中的一个特例,因为它不绑定到不同的编译时路径签名,并且可以在任何实现对象中被覆盖。

要修复给定代码中的编译错误,您可以使用冲突类型之一显式覆盖特征 A2 中的冲突类型成员 EE,如下所示:

object Case1 {

  trait Sub {
    type EE
  }

  trait S1 extends Sub { type EE = Product }
  trait S2 extends Sub { type EE = Tuple }

  trait A1 extends S1

  trait A2 extends A1 with S2 {
    override type EE = Tuple // or Product
  }
}

这告诉编译器 A2 中的 EE 使用哪种类型,解决冲突。请注意,您可以根据您的要求选择使用 Product 或 Tuple 覆盖 EE。

© www.soinside.com 2019 - 2024. All rights reserved.