【问题标题】:Abstract type member of a singleton object单例对象的抽象类型成员
【发布时间】:2020-06-18 18:34:47
【问题描述】:

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

scala> object Foo {
     |   def g: Int
     | }
         def g: Int
             ^
On line 2: error: only traits and abstract classes can have declared but undefined members

作为抽象值成员

scala> object Foo {
     |   val x: Int
     | }
         val x: Int
             ^
On line 2: error: only traits and abstract classes can have declared but undefined members

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

scala> object Foo {
     |   type A
     | }
object Foo

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

【问题讨论】:

  • 投稿人频道里有关于这个的讨论,你可以搜索一下。这是有原因的,但我真的不明白。
  • @LuisMiguelMejíaSuárez 你的意思是What’s in a type alias?似乎它不是我能收集到的抽象。
  • 我相信是这个one,但我不记得是否只有这个。

标签: scala singleton overriding type-alias abstract-type


【解决方案1】:

嗯,type 不一定是具体的。你可以写:

type Arbitrary

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

type Arbitrary

def foo(a: Arbitrary): List[Arbitrary] = List(a)

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

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

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

type Arbitrary = String
type Arbitrary <: AnyVal
type Arbitrary >: User

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

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

在 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"))

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

【讨论】:

  • 这似乎在没有type Arbitrary 的情况下工作,只需使用存在的def instance[F[_]](fun: F[_] =&gt; F[_])scastie
  • 是的,这是实现它的另一种方式,但如果我们考虑一下,它是相同的概念,只是其中一个使事情更明确。如果你扩展存在类型,你会看到什么? X forSome { type X } - 空类型成员,只是在不同的地方定义。
  • @MarioGalic 不,这不起作用。尝试将DoubleSize.instance[List] { list =&gt; List(42) }.double(List("x")).head.size 添加到您的 scastie sn-p。它会愉快地编译,但随后会因 classcastexception 而崩溃。
【解决方案2】:

基于@tpolecatDmytro,抽象类型成员似乎与存在类型相关

scala> object O {
     |   type A
     |   implicitly[A <:< (x forSome { type x })]
     | }
object O

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    相关资源
    最近更新 更多