2017-02-10 119 views
3

(斯卡拉2.11.8)继承和自递归类型推断

我有复杂的自递归型签名的性状GenTableLike限定用于级联兼容表实现方法++。我也有一个层次GenTableLike >: KeyTable >: MapKeyTable。但是,++'两个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 

难道我做错了什么吗?

备注:

自我类型和转置类型用于定义函数返回类型。我也有一个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] 
+0

的也许一个https://github.com/slamdata/matryoshka https://www.youtube.com/watch?v=rBmx9NsNSXM&t=1975s https://开头tpolecat.github.io/2013/10/12/typeclass.html可能会有所帮助。 – Reactormonk

回答

0

我设法找到了我的问题的解决方案,我认为是比PH88的建议更好。我从SelfType/TransposedType除去约束和在https://issues.scala-lang.org/browse/SI-8039

该溶液的副作用的描述中++参数类型使用ST forSome { type ST[+A2] }语法,如:在方面

  1. 不能在GenTableLike限定的方法实现其他方法(因为它们会返回一个未知性质的SelfType) - 即不能重用GenTableLike代码中的方法。这可以通过以下GenTableLike及以上KeyTableIndexedTable提供额外的继承层workarounded
  2. 子类可以定义非表作为其自我/换位类型,但是这似乎并不像一个大问题 - 我们没有理由为他们做所以。

结果代码:

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 
1

如何打开SelfTypeTransposeType到抽象类型?这是简单的,并且工作:

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] 
} 

响应Alex的评论更新于2月15日:

递归式+继承是棘手的,我一直避免它,如果可能的:-)。
如果SelfTypeTransposeType是作为函数的返回类型只,如何消除SelfTypeTransposeType干脆使用隐建设者,如:

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 
+0

我试过了,这种方法的问题是你不能扩展MapKeyTable并覆盖它的SelfType'' MapTypeTable'定义'SelfType'使用'='而不是'<:'。另一方面,你不能用'<:'声明'SelfType',因为它不允许你从'++'返回MapKeyTable'(这很有道理 - 如果孩子类覆盖'SelfType'而不是'++') –

+0

(关于你的第二个解决方案)这是解决它的一种方法,谢谢你。如果我说得对,这是斯卡拉收藏的一些东西,但更简单。问题在于我们必须标准化构建器应该接受哪些内容才能构建表,而不同的实现可能会对此有不同的意见 - 强制它们成为单一格式将导致无效的实现。 –

+0

虽然我仍然会奖励你为你付出的努力。谢谢! –