单例对象的抽象类型成员 [英] Abstract type member of a singleton object
问题描述
抽象成员方法在单例对象中是非法的
scala>对象 Foo {|def g: 整数|}def g: 整数^第 2 行:错误:只有特征和抽象类可以声明但未定义的成员
作为抽象值成员
scala>对象 Foo {|val x: 整数|}val x: 整数^第 2 行:错误:只有特征和抽象类可以声明但未定义的成员
但是抽象类型成员在单例对象中是合法的
scala>对象 Foo {|A型|}对象 Foo
很明显,类型成员是抽象的意义不同于其他抽象成员.有什么区别?抽象类型成员如何能有用,因为对象是最终的,它似乎无法具体化?
嗯,事情是 type
不必是具体的.你可以写:
类型任意
在 Ammonite 中编译并运行.您甚至可以将其用作参数!
类型任意def foo(a: Arbitrary): List[Arbitrary] = List(a)
唯一的问题是,编译器对 Arbitrary
一无所知(例如它<: String
或其他东西)这将允许您合法地创建一个这种类型的值.
从这个角度来看,抽象类型成员只是一种我们一无所知的类型,但我们可以只知道它存在并且值属于这种类型.
但是,我们也可以覆盖
这个空定义,使其更具体,例如
type Arbitrary = String任意类型<:AnyVal输入任意 >: 用户
那么任何实现它的人都可以访问完整的类型信息,而编写在范围内具有抽象定义的代码的程序员只能传递类型.
偶然地,这就是我们重新发现路径依赖类型的方式,或者更确切地说,如果我们想要拥有路径依赖类型而无需手动维护我们不想要的情况列表,那么能够创建抽象类型是必要的
在 Scala 2 中的 Cats 中有另一个用例.Edward Kmett 显然发现了一种使用抽象类型的模式member 和 .asInstanceOf
来解决缺少我们可以提升到类型类的多态函数:
如果我们排除抽象类型成员,这种变通方法将是不可能的,尽管在 Dotty 中,这种特殊的变通方法不再是必要的.
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
(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.
这篇关于单例对象的抽象类型成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!