简体   繁体   中英

Abstract type member of a singleton object

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?

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 (eg 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 eg

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM