单例对象的抽象类型成员
Abstract type member of 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
作为抽象值成员
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
很明显,类型成员抽象的意义不同于其他抽象成员。有什么不同?由于对象是最终的,抽象类型成员似乎无法具体化,它怎么会有用?
好吧,事情 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
那么无论谁实现它都可以访问完整的类型信息,而编写具有范围内抽象定义的代码的程序员只能传递类型。
意外地,这就是我们重新发现路径相关类型的方式,或者更确切地说,如果我们想要拥有路径相关类型而不需要手动维护我们不希望的情况列表,那么能够创建抽象类型是一种必要的方式'我不想要它们。
在 Cats in Scala 2 中,这有另一个用例。 Edward Kmett 显然发现了一种模式,它使用抽象类型成员和 .asInstanceOf
来解决缺少的多态函数,我们可以将其提升为类型 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"))
如果我们排除抽象类型成员,这种变通方法就不可能实现,尽管在 Dotty 中不再需要这种特殊的变通方法。
基于@tpolecat and ,抽象类型成员似乎与存在类型相关
scala> object O {
| type A
| implicitly[A <:< (x forSome { type x })]
| }
object O
抽象成员方法在单例对象中是非法的
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
很明显,类型成员抽象的意义不同于其他抽象成员。有什么不同?由于对象是最终的,抽象类型成员似乎无法具体化,它怎么会有用?
好吧,事情 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
那么无论谁实现它都可以访问完整的类型信息,而编写具有范围内抽象定义的代码的程序员只能传递类型。
意外地,这就是我们重新发现路径相关类型的方式,或者更确切地说,如果我们想要拥有路径相关类型而不需要手动维护我们不希望的情况列表,那么能够创建抽象类型是一种必要的方式'我不想要它们。
在 Cats in Scala 2 中,这有另一个用例。 Edward Kmett 显然发现了一种模式,它使用抽象类型成员和 .asInstanceOf
来解决缺少的多态函数,我们可以将其提升为类型 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"))
如果我们排除抽象类型成员,这种变通方法就不可能实现,尽管在 Dotty 中不再需要这种特殊的变通方法。
基于@tpolecat and
scala> object O {
| type A
| implicitly[A <:< (x forSome { type x })]
| }
object O