继承和自递归类型推断
Inheritance and self-recursive type inference
(Scala 2.11.8)
我有一个特征 GenTableLike
具有复杂的自递归类型签名,它定义了方法 ++
以连接兼容的 table 实现。我也有一个层次结构 GenTableLike >: KeyTable >: MapKeyTable
。但是,++
'ing 两个 MapKeyTable
无法推断自递归类型。
这里是一个稍微简化的片段,其中问题仍然重现:
trait GenTableLike[RKT,
CKT,
+A,
+SelfType[+A2] <: GenTableLike[RKT, CKT, A2, SelfType, TransposedType],
+TransposedType[+A2] <: GenTableLike[CKT, RKT, A2, TransposedType, SelfType]] {
def ++[B >: A,
T2 <: GenTableLike[RKT, CKT, B, ST, TT],
ST[+A2] <: GenTableLike[RKT, CKT, A2, ST, TT],
TT[+A2] <: GenTableLike[CKT, RKT, A2, TT, ST]](that: T2): SelfType[B] = ???
}
trait KeyTable[RKT, CKT, +A]
extends GenTableLike[RKT,
CKT,
A,
KeyTable.Curried[RKT, CKT]#Self,
KeyTable.Curried[CKT, RKT]#Self]
object KeyTable {
/** Type helper for defining self-recursive type */
type Curried[RKT, CKT] = {
type Self[+A] = KeyTable[RKT, CKT, A]
}
}
class MapKeyTable[RKT, CKT, +A]
extends KeyTable[RKT, CKT, A]
with GenTableLike[RKT,
CKT,
A,
MapKeyTable.Curried[RKT, CKT]#Self,
MapKeyTable.Curried[CKT, RKT]#Self]
object MapKeyTable {
/** Type helper for defining self-recursive type */
type Curried[RKT, CKT] = {
type Self[+A] = MapKeyTable[RKT, CKT, A]
}
}
val t1: MapKeyTable[Int, String, Int] = ???
val t2: MapKeyTable[Int, String, Any] = ???
// The following works
t1.++[Any, MapKeyTable[Int, String, Any], ({ type ST[+A2] = MapKeyTable[Int, String, A2] })#ST, ({ type TT[+A2] = MapKeyTable[String, Int, A2] })#TT](t2)
t1.++[Any, MapKeyTable[Int, String, Any], MapKeyTable.Curried[Int, String]#Self, MapKeyTable.Curried[String, Int]#Self](t2)
// Error: inferred type arguments [Int,MapKeyTable[Int,String,Any],Nothing,Nothing] do not conform to method ++'s type parameter bounds [B >: Int,T2 <: GenTableLike[Int,String,B,ST,TT],ST[+A2] <: GenTableLike[Int,String,A2,ST,TT],TT[+A2] <: GenTableLike[String,Int,A2,TT,ST]]
t1 ++ t2
我是不是做错了什么?
备注:
自类型和转置类型用于定义函数return类型。我还有一个如下定义的 IndexedTable 实现,所以我无法重新编写自己的类型来接受 3 个参数
trait IndexedTable[+A]
extends GenTableLike[Int,
Int,
A,
IndexedTable,
IndexedTable]
class IndexedSeqTable[+A]
extends IndexedTable[A]
with GenTableLike[Int,
Int,
A,
IndexedSeqTable,
IndexedSeqTable]
如何将 SelfType
和 TransposeType
转换为抽象类型?这更简单,并且有效:
import scala.language.higherKinds
trait GenTableLike[RKT, CKT, +A] {
type SelfType[+A2] <: GenTableLike[RKT, CKT, A2]
type TransposedType[+A2] <: GenTableLike[CKT, RKT, A2]
def ++[B >: A](that: GenTableLike[RKT, CKT, B]): SelfType[B]
}
trait KeyTable[RKT, CKT, +A]
extends GenTableLike[RKT, CKT, A] {
override type SelfType[+A2] <: KeyTable[RKT, CKT, A2]
override type TransposedType[+A2] <: KeyTable[CKT, RKT, A2]
}
class MapKeyTable[RKT, CKT, +A]
extends KeyTable[RKT, CKT, A] {
override type SelfType[+A2] = MapKeyTable[RKT, CKT, A2]
override type TransposedType[+A2] = MapKeyTable[CKT, RKT, A2]
override def ++[B >: A](that: GenTableLike[RKT, CKT, B]): MapKeyTable[RKT, CKT, B] =
new MapKeyTable[RKT, CKT, B]
}
val t1 = new MapKeyTable[Int, String, Int]
val t2 = new MapKeyTable[Int, String, Any]
// The following works
t1.++[Any](t2)
t1 ++ t2
trait IndexedTable[+A]
extends GenTableLike[Int, Int, A] {
override type SelfType[+A2] <: IndexedTable[A2]
override type TransposedType[+A2] <: IndexedTable[A2]
}
class IndexedSeqTable[+A]
extends IndexedTable[A] {
override type SelfType[+A2] = IndexedSeqTable[A2]
override type TransposedType[+A2] = IndexedSeqTable[A2]
override def ++[B >: A](that: GenTableLike[Int, Int, B]): IndexedSeqTable[B] = new IndexedSeqTable[B]
}
2 月 15 日更新以回应 Alex 的评论:
递归类型 + 继承是棘手的,我总是尽可能避免它:-)。
如果 SelfType
和 TransposeType
仅用作函数 return 类型,如何完全消除 SelfType
和 TransposeType
并使用隐式构建器,如:
import scala.language.higherKinds
trait GenTableLike[RKT, CKT, +A] {
def value: A
def ++[B >: A](that: GenTableLike[RKT, CKT, B])(implicit builder: GenTableLike.Builder[this.type, B]): builder.Self = {
builder.buildSelf(that.value)
}
def transpose(implicit builder: GenTableLike.Builder[this.type, A]) = builder.buildTranspose(value)
}
object GenTableLike {
trait Builder[-This, -A] {
type Self
type Transpose
def buildSelf(a: A): Self
def buildTranspose(a: A): Transpose
}
}
trait KeyTable[RKT, CKT, +A]
extends GenTableLike[RKT, CKT, A] {
}
class MapKeyTable[RKT, CKT, +A](override val value: A)
extends KeyTable[RKT, CKT, A] {
}
object MapKeyTable {
implicit def builder[RKT, CKT, A] = new GenTableLike.Builder[MapKeyTable[RKT, CKT, A], A] {
override type Self = MapKeyTable[RKT, CKT, A]
override type Transpose = MapKeyTable[CKT, RKT, A]
override def buildSelf(a: A): Self = new MapKeyTable[RKT, CKT, A](a)
override def buildTranspose(a: A): Transpose = new MapKeyTable[CKT, RKT, A](a)
}
}
class MapKeyTableEx[RKT, CKT, +A](override val value: A)
extends MapKeyTable[RKT, CKT, A](value)
object MapKeyTableEx {
implicit def builder[RKT, CKT, A] = new GenTableLike.Builder[MapKeyTableEx[RKT, CKT, A], A] {
override type Self = MapKeyTableEx[RKT, CKT, A]
override type Transpose = MapKeyTableEx[CKT, RKT, A]
override def buildSelf(a: A): Self = new MapKeyTableEx[RKT, CKT, A](a)
override def buildTranspose(a: A): Transpose = new MapKeyTableEx[CKT, RKT, A](a)
}
}
val t1 = new MapKeyTable[Int, String, Int](1)
val t2 = new MapKeyTable[Int, String, Any]("b")
val t3 = new MapKeyTableEx[Int, String, Int](1)
val t4 = new MapKeyTableEx[Int, String, Any]("b")
// The following works
t1 ++ t2
t1 ++ t3
t2 ++ t3
t3 ++ t4
t1.transpose
t2.transpose
t3.transpose
t4.transpose
我已经找到了我认为比@PH88 的建议更好的问题的解决方案。我从 SelfType
/TransposedType
中删除了约束,并在 ++
参数类型中使用了 ST forSome { type ST[+A2] }
语法,就像 https://issues.scala-lang.org/browse/SI-8039
的描述一样
此解决方案的副作用:
- 不能根据其他方法定义
GenTableLike
中的方法实现(因为它们将 return 一个 SelfType
性质未知)——即不能在内部重用方法GenTableLike
代码。这可以通过在 GenTableLike
以下和 KeyTable
和 IndexedTable
以上提供额外的继承层来解决
- 子类可以将 non-tables 定义为其 self/transposed 类型,但这似乎不是什么大问题 - 他们没有理由这样做。
结果代码:
trait GenTableLike[RKT,
CKT,
+A,
+SelfType[+A2],
+TransposedType[+A2]] {
def ++[B >: A](that: GenTableLike[RKT,
CKT,
B,
ST forSome { type ST[+A2] },
TT forSome { type TT[+A2] }]): SelfType[B] = ???
}
// Common ancestor for all table classes
trait GenTable[RKT, CKT, +A]
extends GenTableLike[RKT,
CKT,
A,
GenTable.Curried[RKT, CKT]#Self,
GenTable.Curried[CKT, RKT]#Self] {
// Here we can implement common methods reusing other methods due to proper SelfType bounds
}
object GenTable {
/** Type helper for defining self-recursive type */
private type Curried[RKT, CKT] = {
type Self[+A] = GenTable[RKT, CKT, A]
}
}
trait KeyTable[RKT, CKT, +A]
extends GenTable[RKT, CKT, A]
with GenTableLike[RKT,
CKT,
A,
KeyTable.Curried[RKT, CKT]#Self,
KeyTable.Curried[CKT, RKT]#Self]
object KeyTable {
/** Type helper for defining self-recursive type */
type Curried[RKT, CKT] = {
type Self[+A] = KeyTable[RKT, CKT, A]
}
}
class MapKeyTable[RKT, CKT, +A]
extends KeyTable[RKT, CKT, A]
with GenTableLike[RKT,
CKT,
A,
MapKeyTable.Curried[RKT, CKT]#Self,
MapKeyTable.Curried[CKT, RKT]#Self] {
override def ++[B >: A](that: GenTableLike[RKT,
CKT,
B,
ST forSome { type ST[+A2] },
TT forSome { type TT[+A2] }]): MapKeyTable[RKT, CKT, B] = {
new MapKeyTable
}
}
object MapKeyTable {
/** Type helper for defining self-recursive type */
type Curried[RKT, CKT] = {
type Self[+A] = MapKeyTable[RKT, CKT, A]
}
}
trait IndexedTable[+A]
extends GenTable[Int, Int, A]
with GenTableLike[Int,
Int,
A,
IndexedTable,
IndexedTable]
class IndexedSeqTable[+A]
extends IndexedTable[A]
with GenTableLike[Int,
Int,
A,
IndexedSeqTable,
IndexedSeqTable] {
override def ++[B >: A](that: GenTableLike[Int,
Int,
B,
ST forSome { type ST[+A2] },
TT forSome { type TT[+A2] }]): IndexedSeqTable[B] = {
new IndexedSeqTable
}
}
// Usage
def s1: IndexedSeqTable[Int] = ???
def s2: IndexedSeqTable[Any] = ???
def t1: MapKeyTable[Int, Int, Int] = ???
def t2: MapKeyTable[Int, Int, Any] = ???
// All of this works with result being of proper type
t1.++[Any](t2)
t1 ++ t2
s1 ++ s2
t1 ++ s1
s1 ++ t1
s2 ++ t1
(Scala 2.11.8)
我有一个特征 GenTableLike
具有复杂的自递归类型签名,它定义了方法 ++
以连接兼容的 table 实现。我也有一个层次结构 GenTableLike >: KeyTable >: MapKeyTable
。但是,++
'ing 两个 MapKeyTable
无法推断自递归类型。
这里是一个稍微简化的片段,其中问题仍然重现:
trait GenTableLike[RKT,
CKT,
+A,
+SelfType[+A2] <: GenTableLike[RKT, CKT, A2, SelfType, TransposedType],
+TransposedType[+A2] <: GenTableLike[CKT, RKT, A2, TransposedType, SelfType]] {
def ++[B >: A,
T2 <: GenTableLike[RKT, CKT, B, ST, TT],
ST[+A2] <: GenTableLike[RKT, CKT, A2, ST, TT],
TT[+A2] <: GenTableLike[CKT, RKT, A2, TT, ST]](that: T2): SelfType[B] = ???
}
trait KeyTable[RKT, CKT, +A]
extends GenTableLike[RKT,
CKT,
A,
KeyTable.Curried[RKT, CKT]#Self,
KeyTable.Curried[CKT, RKT]#Self]
object KeyTable {
/** Type helper for defining self-recursive type */
type Curried[RKT, CKT] = {
type Self[+A] = KeyTable[RKT, CKT, A]
}
}
class MapKeyTable[RKT, CKT, +A]
extends KeyTable[RKT, CKT, A]
with GenTableLike[RKT,
CKT,
A,
MapKeyTable.Curried[RKT, CKT]#Self,
MapKeyTable.Curried[CKT, RKT]#Self]
object MapKeyTable {
/** Type helper for defining self-recursive type */
type Curried[RKT, CKT] = {
type Self[+A] = MapKeyTable[RKT, CKT, A]
}
}
val t1: MapKeyTable[Int, String, Int] = ???
val t2: MapKeyTable[Int, String, Any] = ???
// The following works
t1.++[Any, MapKeyTable[Int, String, Any], ({ type ST[+A2] = MapKeyTable[Int, String, A2] })#ST, ({ type TT[+A2] = MapKeyTable[String, Int, A2] })#TT](t2)
t1.++[Any, MapKeyTable[Int, String, Any], MapKeyTable.Curried[Int, String]#Self, MapKeyTable.Curried[String, Int]#Self](t2)
// Error: inferred type arguments [Int,MapKeyTable[Int,String,Any],Nothing,Nothing] do not conform to method ++'s type parameter bounds [B >: Int,T2 <: GenTableLike[Int,String,B,ST,TT],ST[+A2] <: GenTableLike[Int,String,A2,ST,TT],TT[+A2] <: GenTableLike[String,Int,A2,TT,ST]]
t1 ++ t2
我是不是做错了什么?
备注:
自类型和转置类型用于定义函数return类型。我还有一个如下定义的 IndexedTable 实现,所以我无法重新编写自己的类型来接受 3 个参数
trait IndexedTable[+A]
extends GenTableLike[Int,
Int,
A,
IndexedTable,
IndexedTable]
class IndexedSeqTable[+A]
extends IndexedTable[A]
with GenTableLike[Int,
Int,
A,
IndexedSeqTable,
IndexedSeqTable]
如何将 SelfType
和 TransposeType
转换为抽象类型?这更简单,并且有效:
import scala.language.higherKinds
trait GenTableLike[RKT, CKT, +A] {
type SelfType[+A2] <: GenTableLike[RKT, CKT, A2]
type TransposedType[+A2] <: GenTableLike[CKT, RKT, A2]
def ++[B >: A](that: GenTableLike[RKT, CKT, B]): SelfType[B]
}
trait KeyTable[RKT, CKT, +A]
extends GenTableLike[RKT, CKT, A] {
override type SelfType[+A2] <: KeyTable[RKT, CKT, A2]
override type TransposedType[+A2] <: KeyTable[CKT, RKT, A2]
}
class MapKeyTable[RKT, CKT, +A]
extends KeyTable[RKT, CKT, A] {
override type SelfType[+A2] = MapKeyTable[RKT, CKT, A2]
override type TransposedType[+A2] = MapKeyTable[CKT, RKT, A2]
override def ++[B >: A](that: GenTableLike[RKT, CKT, B]): MapKeyTable[RKT, CKT, B] =
new MapKeyTable[RKT, CKT, B]
}
val t1 = new MapKeyTable[Int, String, Int]
val t2 = new MapKeyTable[Int, String, Any]
// The following works
t1.++[Any](t2)
t1 ++ t2
trait IndexedTable[+A]
extends GenTableLike[Int, Int, A] {
override type SelfType[+A2] <: IndexedTable[A2]
override type TransposedType[+A2] <: IndexedTable[A2]
}
class IndexedSeqTable[+A]
extends IndexedTable[A] {
override type SelfType[+A2] = IndexedSeqTable[A2]
override type TransposedType[+A2] = IndexedSeqTable[A2]
override def ++[B >: A](that: GenTableLike[Int, Int, B]): IndexedSeqTable[B] = new IndexedSeqTable[B]
}
2 月 15 日更新以回应 Alex 的评论:
递归类型 + 继承是棘手的,我总是尽可能避免它:-)。
如果 SelfType
和 TransposeType
仅用作函数 return 类型,如何完全消除 SelfType
和 TransposeType
并使用隐式构建器,如:
import scala.language.higherKinds
trait GenTableLike[RKT, CKT, +A] {
def value: A
def ++[B >: A](that: GenTableLike[RKT, CKT, B])(implicit builder: GenTableLike.Builder[this.type, B]): builder.Self = {
builder.buildSelf(that.value)
}
def transpose(implicit builder: GenTableLike.Builder[this.type, A]) = builder.buildTranspose(value)
}
object GenTableLike {
trait Builder[-This, -A] {
type Self
type Transpose
def buildSelf(a: A): Self
def buildTranspose(a: A): Transpose
}
}
trait KeyTable[RKT, CKT, +A]
extends GenTableLike[RKT, CKT, A] {
}
class MapKeyTable[RKT, CKT, +A](override val value: A)
extends KeyTable[RKT, CKT, A] {
}
object MapKeyTable {
implicit def builder[RKT, CKT, A] = new GenTableLike.Builder[MapKeyTable[RKT, CKT, A], A] {
override type Self = MapKeyTable[RKT, CKT, A]
override type Transpose = MapKeyTable[CKT, RKT, A]
override def buildSelf(a: A): Self = new MapKeyTable[RKT, CKT, A](a)
override def buildTranspose(a: A): Transpose = new MapKeyTable[CKT, RKT, A](a)
}
}
class MapKeyTableEx[RKT, CKT, +A](override val value: A)
extends MapKeyTable[RKT, CKT, A](value)
object MapKeyTableEx {
implicit def builder[RKT, CKT, A] = new GenTableLike.Builder[MapKeyTableEx[RKT, CKT, A], A] {
override type Self = MapKeyTableEx[RKT, CKT, A]
override type Transpose = MapKeyTableEx[CKT, RKT, A]
override def buildSelf(a: A): Self = new MapKeyTableEx[RKT, CKT, A](a)
override def buildTranspose(a: A): Transpose = new MapKeyTableEx[CKT, RKT, A](a)
}
}
val t1 = new MapKeyTable[Int, String, Int](1)
val t2 = new MapKeyTable[Int, String, Any]("b")
val t3 = new MapKeyTableEx[Int, String, Int](1)
val t4 = new MapKeyTableEx[Int, String, Any]("b")
// The following works
t1 ++ t2
t1 ++ t3
t2 ++ t3
t3 ++ t4
t1.transpose
t2.transpose
t3.transpose
t4.transpose
我已经找到了我认为比@PH88 的建议更好的问题的解决方案。我从 SelfType
/TransposedType
中删除了约束,并在 ++
参数类型中使用了 ST forSome { type ST[+A2] }
语法,就像 https://issues.scala-lang.org/browse/SI-8039
此解决方案的副作用:
- 不能根据其他方法定义
GenTableLike
中的方法实现(因为它们将 return 一个SelfType
性质未知)——即不能在内部重用方法GenTableLike
代码。这可以通过在GenTableLike
以下和KeyTable
和IndexedTable
以上提供额外的继承层来解决
- 子类可以将 non-tables 定义为其 self/transposed 类型,但这似乎不是什么大问题 - 他们没有理由这样做。
结果代码:
trait GenTableLike[RKT,
CKT,
+A,
+SelfType[+A2],
+TransposedType[+A2]] {
def ++[B >: A](that: GenTableLike[RKT,
CKT,
B,
ST forSome { type ST[+A2] },
TT forSome { type TT[+A2] }]): SelfType[B] = ???
}
// Common ancestor for all table classes
trait GenTable[RKT, CKT, +A]
extends GenTableLike[RKT,
CKT,
A,
GenTable.Curried[RKT, CKT]#Self,
GenTable.Curried[CKT, RKT]#Self] {
// Here we can implement common methods reusing other methods due to proper SelfType bounds
}
object GenTable {
/** Type helper for defining self-recursive type */
private type Curried[RKT, CKT] = {
type Self[+A] = GenTable[RKT, CKT, A]
}
}
trait KeyTable[RKT, CKT, +A]
extends GenTable[RKT, CKT, A]
with GenTableLike[RKT,
CKT,
A,
KeyTable.Curried[RKT, CKT]#Self,
KeyTable.Curried[CKT, RKT]#Self]
object KeyTable {
/** Type helper for defining self-recursive type */
type Curried[RKT, CKT] = {
type Self[+A] = KeyTable[RKT, CKT, A]
}
}
class MapKeyTable[RKT, CKT, +A]
extends KeyTable[RKT, CKT, A]
with GenTableLike[RKT,
CKT,
A,
MapKeyTable.Curried[RKT, CKT]#Self,
MapKeyTable.Curried[CKT, RKT]#Self] {
override def ++[B >: A](that: GenTableLike[RKT,
CKT,
B,
ST forSome { type ST[+A2] },
TT forSome { type TT[+A2] }]): MapKeyTable[RKT, CKT, B] = {
new MapKeyTable
}
}
object MapKeyTable {
/** Type helper for defining self-recursive type */
type Curried[RKT, CKT] = {
type Self[+A] = MapKeyTable[RKT, CKT, A]
}
}
trait IndexedTable[+A]
extends GenTable[Int, Int, A]
with GenTableLike[Int,
Int,
A,
IndexedTable,
IndexedTable]
class IndexedSeqTable[+A]
extends IndexedTable[A]
with GenTableLike[Int,
Int,
A,
IndexedSeqTable,
IndexedSeqTable] {
override def ++[B >: A](that: GenTableLike[Int,
Int,
B,
ST forSome { type ST[+A2] },
TT forSome { type TT[+A2] }]): IndexedSeqTable[B] = {
new IndexedSeqTable
}
}
// Usage
def s1: IndexedSeqTable[Int] = ???
def s2: IndexedSeqTable[Any] = ???
def t1: MapKeyTable[Int, Int, Int] = ???
def t2: MapKeyTable[Int, Int, Any] = ???
// All of this works with result being of proper type
t1.++[Any](t2)
t1 ++ t2
s1 ++ s2
t1 ++ s1
s1 ++ t1
s2 ++ t1