Eliminate duplicate code, the only difference being the type

I have two structurally equivalent pairs of classes, the only difference being the types. I would like to combine V1

with V2

and K1

using K2

. I am managing these 4 classes, but I cannot change takesDoublesOrLists

- it comes from the library.

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

      

I tried following the type class pattern , but got the error that the overloaded method takesDoublesOrLists

could not be applied to my type parameter.

Edit:

The overloaded function signature takesDoublesOrLists

matches the name:

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

      

+3


source to share


2 answers


I see how you could combine V1

and V2

, but not K1

or 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)
}

      



EDIT: If your K1 and K2 classes are more complex than you demonstrated, you can break this behavior with traits:

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 {
  // ...
}

      

+1


source


I tried following the type pattern but got the error that the overloaded getsDoublesOrLists method cannot be applied to my type parameter.

You don't show your attempt, but you must not call the overloaded version takesDoublesOrLists

, it must be part of the class (of course, you can call the existing version when implementing it if there is a reason not to port the implementation there):



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

      

+1


source







All Articles