可以简化复合仿制药吗?如何避免[T1与T2]?

时间:2016-05-03 00:41:34

标签: scala generics

考虑

trait E[C] {
  type CONTEXT = C
  def doIt(c:CONTEXT): Unit = {}
}

// My doIt method takes a C
case class ESimple[C]() extends E[C] 

// ECompound's doIt method must satisfy the requirements of both
// a's and b's doIt parameters.
case class ECompound[C1,C2](a:E[C1],b:E[C2]) extends E[C1 with C2] {
  override def doIt(c: CONTEXT): Unit = {
    a.doIt(c)
    b.doIt(c)
  }
}

......等等:

case class Context 
val context = Context()

val a:E[Context] = ESimple[Context]()
val b:E[Context] = ESimple[Context]()
val c:E[Context] = ECompound(a,b)  // type mismatch

这是因为

val c:E[Context with Context] = ECompound(a,b)  // type not mismatched

是报告的ECompound类型(a,b)。那么,有可能解决这个问题吗? ECompound(a,b).doIt真的应该只需要一个" Context",它不会简化为" Context with Context"

我意识到我可以通过更改" ...扩展E [C1 with C2]"来解决这个问题。扩展[C1],但代价是要求C1 == C2,我想避免。

3 个答案:

答案 0 :(得分:1)

一种可能的方法是将ECompound定义为:

 case class ECompound[C1,C2, CC <: C1 with C2](a:E[C1],b:E[C2]) extends E[CC]

缺点是,如果您未明确指定CC,则会将其推断为Nothing,即:

val c: E[Context] =
  ECompound(ESimple[Context](), ESimple[Context]()) // works

val c1: E[Context with Context1] =
  ECompound(ESimple[Context](), ESimple[Context1]()) // works

val c2 =
  ECompound(ESimple[Context](), ESimple[Context1]()) 
  // works, but inferred type is ECompound[Context,Context1,Nothing]

答案 1 :(得分:1)

您可以考虑将ECompound定义为:

case class ECompound[CC, C1 >: CC, C2 >: CC](a:E[C1],b:E[C2]) extends E[CC] {
  override def doIt(c: CONTEXT): Unit = {
    a.doIt(c)
    b.doIt(c)
  }
}

答案 2 :(得分:0)

嗯......这太尴尬了。

原来我正在编译scala 2.10。使用2.11.8,我原来很好,只有一个小的改变E:

trait E[-C] {  // Added -C
  def doIt(c: C): Unit = {}
  def asE: E[C] = this // see below
}

case class ESimple[C]() extends E[C]

//case class ECompound[C1, C2, CC <: C1 with C2](a: E[C1], b: E[C2]) extends E[CC] {
//case class ECompound[CC, C1 >: CC, C2 >: CC](a:E[C1],b:E[C2]) extends E[CC] {
case class ECompound[C1, C2](a: E[C1], b: E[C2]) extends E[C1 with C2] {
  override def doIt(c: C1 with C2): Unit = {
    a.doIt(c)
    b.doIt(c)
  }
}

trait Context1
trait Context2

val e1: E[Context1] = ESimple[Context1]()
val e2: E[Context2] = ESimple[Context2]()

val cc11: E[Context1] = ECompound(e1, e1)
val cc12: E[Context1 with Context2] = ECompound(e1, e2)

case class X()

cc12.doIt(new X with Context1 with Context2)

然而,关于这一点的丑陋部分是:

def compileTimeType[T](x: T)(implicit tag: TypeTag[T]) = tag.tpe

val a = ECompound(e1, e1)
val b = ECompound(a, a)
val c = ECompound(b, b)
println( compileTimeType( c.asE ) )

给出:

com.example.E[com.example.Context1
    with com.example.Context1
    with com.example.Context1
    with com.example.Context1
    with com.example.Context1
    with com.example.Context1
    with com.example.Context1
    with com.example.Context1]

(不失一般性),可以简化为:

com.example.E[com.example.Context1]

但不是。当然会产生一些讨厌的编译器错误......

如果你可以避免这种混乱,这个答案就是你的。