Scala 消除唯一不同的是类型的重复代码

Scala 消除唯一不同的是类型的重复代码,scala,Scala,我有两个结构上等价的类对,它们唯一的区别是类型。我希望能够将V1与V2和K1与K2合并。我控制这四个类,但我无法修改takesDoublesOrLists——它来自一个库 class V1 ( val a: Double, val b: Double, // ... val z: Double ) class K1(v: V1) { def doIt = takesDoublesOrLists(v.a, v.b, /* ..., */ v.z) } c

我有两个结构上等价的类对,它们唯一的区别是类型。我希望能够将
V1
V2
K1
K2
合并。我控制这四个类,但我无法修改
takesDoublesOrLists
——它来自一个库

class V1 (
    val a: Double,
    val b: Double,
    // ...
    val z: Double
)

class K1(v: V1) {
    def doIt = takesDoublesOrLists(v.a, v.b, /* ..., */ v.z)
}

class V2 (
    val a: List[Double],
    val b: List[Double],
    // ...
    val z: List[Double]
)

class K2(v: V2) {
    def doIt = takesDoublesOrLists(v.a, v.b, /* ..., */ v.z)
}
我尝试遵循,但出现了一个错误,重载方法
takesDoublesOrLists
无法应用于我的类型参数

编辑:

重载函数
takesDoublesOrLists
的签名如名称所示:

def takesDoublesOrLists(a: Double, b: Double, /* ..., */ z: Double): Something = /* ... */
def takesDoublesOrLists(a: List[Double], b: List[Double], /* ..., */ z: List[Double]): Something = /* ... */

我可以看到如何合并
V1
V2
,但不能合并
K1
K2

class V[T] (
  val a: T,
  val b: T
  // ...
  val z: T
)

class K1(v: V[Double]) {
  def doIt = takesDoublesOrLists(v.a, v.b, /* ..., */ v.z)
}

class K2(v: V[List[Double]]) {
  def doIt = takesDoublesOrLists(v.a, v.b, /* ..., */ v.z)
}
编辑:如果你的K1和K2类比你举例说明的更复杂,你可以用以下特征打破这种行为:

trait KDoubles {
  def v: V[Double]
  def doIt = takesDoublesOrLists(v.a, v.b, /* ..., */ v.z)
}

trait KLists {
  def v: V[List[Double]]
  def doIt = takesDoublesOrLists(v.a, v.b, /* ..., */ v.z)
}

class K {
  // common stuff ...
}

class K1 extends K with KDoubles {
  // ...
}

class K2 extends K with KLists {
  // ...
}
我尝试遵循类型类模式,但出现了一个错误,重载方法takesDoublesOrLists无法应用于我的类型参数

您没有显示您的尝试,但不应该调用重载版本的
takesDoublesOrLists
,它应该是typeclass的一部分(当然,如果有理由不将实现移到那里,您可以在实现时调用现有版本):


你能分享
TakesDoubleSorList
的签名吗?检查:@TzachZohar:我在上面的编辑中添加了。是什么阻止你只拥有一个K和一个V类,因为它们完全相同(至少在你的代码中是如此)…。@ericbn:它们不是:在V1中,每个val都是
,而在V2中,它们都是
列表[Double]类型
,和
接受双重排序列表
需要知道这种类型。这是一个合理的建议,所以请进行投票。如果没有其他可能,这是值得的,但是
K1
K2
V
复杂得多,是我更大的目标-我只是将它们归结为一个函数来说明问题的核心。@Nate,更新了我的答案。我们需要更多关于K1和K2的信息,以便能够更好地解释它们……这是一个很好的观点。不幸的是,提取问题是一门不精确的科学。在我的例子中,
takesDoublesOrLists
是一些函数的替代品,这些函数在
K1
K2
的每个部分都被广泛使用。我想我的例子足以抓住我问题的核心,而不会让人分心。我想,这正是问题所在:
takes双倍分类列表
不是类型类的成员。我创建的typeclass是空的,只是用作
V
K
中的限制<代码>takesDoublesOrLists
是我无法控制的代码。如果我把它带到typeclass中,我会回到我开始的地方。正如我所说的,在这种情况下,typeclass实例中的
takesDoublesOrLists
的实现应该调用那些你不控制的。我已经编辑并更改了typeclass方法的名称,使其更加清晰。是的,我从您最初的示例中理解了。我不清楚什么时候我会回到我开始的地方;我的意思是,我将把
K
的两个实现的复杂性从一层转移到了typeclass的两个几乎重复的实现。就字符数而言,它可能大致相等。我在争论这是否是一个更简单的解决方案。
sealed trait DoubleOrList[A] {
  def doIt(a: A, b: A, /* ..., */ z: A): Something
}

object DoubleOrList {
  implicit object DoubleIsDoubleOrList extends DoubleOrList[Double] {
    def doIt(a: Double, b: Double, /* ..., */ z: Double): Something = 
      SomeObject.takesDoublesOrLists(a, b, z)
  }
  implicit object ListIsDoubleOrList extends DoubleOrList[List[Double]] { 
    def doIt(a: List[Double], b: List[Double], /* ..., */ z: List[Double]): Something = 
      SomeObject.takesDoublesOrLists(a, b, z)
  }
}

// this context bound isn't really necessary, but it prevents you from creating V[String] you can't use
class V[A: DoubleOrList](
  val a: A,
  val b: A,
  // ...
  val z: A
)

class K[A](v: V[A])(implicit dOrL: DoubleOrList[A]) {
  def doIt = dOrL.doIt(v.a, v.b, /* ..., */ v.z)
}