Abstract type member of a singleton object

家住魔仙堡 提交于 2021-02-08 08:31:51

问题


Abstract member method is illegal in a singleton object

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

as is abstract value member

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

however abstract type member is legal in a singleton object

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

so clearly the sense in which a type member is abstract is different from other abstract members. What is the difference? How can an abstract type member be useful when it seems it cannot be made concrete since object is final?


回答1:


Well, the things is type doesn't have to be concrete. You can write:

type Arbitrary

in Ammonite and it compiles and runs. You can even use it as argument!

type Arbitrary

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

The only issue is, that compiler doesn't know anything about Arbitrary (e.g. that it <: String or something) which would allow you to legally create a value of this type.

From that point of view, abstract type member is just a type we don't know anything about, but which we can around knowing only that it exist and value would be of this type.

But, we can also override this empty definition by making it more specific e.g.

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

Then whoever implements it will have access to full type information, while programmer writing code that has abstract definition in scope can only pass type around.

Accidentally, that's how we rediscovered path-dependent types, or rather figured out that being able to create abstract types is kind of necessary if we want to have path-dependent types without manually maintained list of cases where we don't want them.

In Cats in Scala 2 this has another use case. There is a pattern which apparently was discovered by Edward Kmett that uses abstract type member and .asInstanceOf to workaround missing polymorphic functions that we could lift to a type class:


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

This kind of workarounds wouldn't be possible if we ruled out abstract type members, though in Dotty this particular workaround won't be necessary anymore.




回答2:


Based on @tpolecat and Dmytro it seems abstract type member is related to an existential type

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



来源:https://stackoverflow.com/questions/62457094/abstract-type-member-of-a-singleton-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!