问题
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