单例对象的抽象类型成员

Mar*_*lic 1 singleton overriding scala abstract-type type-alias

抽象成员方法在单例对象中是非法的

scala> object Foo {
     |   def g: Int
     | }
         def g: Int
             ^
On line 2: error: only traits and abstract classes can have declared but undefined members
Run Code Online (Sandbox Code Playgroud)

as is 抽象值成员

scala> object Foo {
     |   val x: Int
     | }
         val x: Int
             ^
On line 2: error: only traits and abstract classes can have declared but undefined members
Run Code Online (Sandbox Code Playgroud)

但是抽象类型成员在单例对象中是合法的

scala> object Foo {
     |   type A
     | }
object Foo
Run Code Online (Sandbox Code Playgroud)

很明显,类型成员抽象的意义与其他抽象成员不同。有什么不同?抽象类型成员如何能有用,因为对象是最终的,它似乎无法具体化?

Mat*_*zok 5

嗯,事情type不必是具体的。你可以写:

type Arbitrary
Run Code Online (Sandbox Code Playgroud)

在 Ammonite 中,它编译并运行。您甚至可以将其用作参数!

type Arbitrary

def foo(a: Arbitrary): List[Arbitrary] = List(a)
Run Code Online (Sandbox Code Playgroud)

唯一的问题是,该编译器不知道Arbitrary(例如,它<: String或其他东西)允许您合法地创建这种类型的值。

从这个角度来看,抽象类型成员只是一种我们一无所知的类型,但我们可以只知道它存在并且值属于这种类型。

但是,我们也可以override通过使其更具体来实现这个空定义,例如

type Arbitrary = String
type Arbitrary <: AnyVal
type Arbitrary >: User
Run Code Online (Sandbox Code Playgroud)

那么无论谁实现它都可以访问完整的类型信息,而编写在范围内具有抽象定义的代码的程序员只能传递类型。

偶然地,这就是我们如何重新发现依赖路径的类型,或者更确切地说,如果我们想要拥有依赖路径的类型而无需手动维护我们不想要它们的情况列表,那么能够创建抽象类型是必要的。

在 Scala 2 中的 Cats 中,这还有另一个用例。Edward Kmett显然发现了一种模式,它使用抽象类型成员并.asInstanceOf解决缺少的多态函数,我们可以将其提升到类型类:


trait DoubleSize[F[_]] {
  def double[A](fa: F[A]): F[A]
}
object DoubleSize {

  type Arbitrary
  def instance[F[_]](fun: F[Arbitrary] => F[Arbitrary]): DoubleSize[F] = new DoubleSize[F] {
    def double[A](fa: F[A]): F[A] = fun(fa.asInstanceOf[F[Arbitrary]]).asInstanceOf[F[A]]
  }

  // in Dotty we could do
  // def instance[F[_]](fun: [A] => F[A] => F[A]) = new DoubleSize[F] {
  //    def double[A](fa: F[A]): F[A] = fun[A](fa)
  // }
  // but in Scala 2 it's impossible
}

val doubleSize = DoubleSize.instance[List] { list =>
  list ++ list
}

doubleSize.double(List(1,2,3))
doubleSize.double(List("a", "b", "c"))
Run Code Online (Sandbox Code Playgroud)

如果我们排除抽象类型成员,这种变通方法将是不可能的,尽管在 Dotty 中,这种特殊的变通方法不再是必要的。

  • @MarioGalic 不,那不起作用。尝试将 `DoubleSize.instance[List] { list =&gt; List(42) }.double(List("x")).head.size` 添加到您的 scastie 代码片段中。它会很高兴地编译,但随后会因类广播异常而崩溃。 (2认同)