allidoiswin
allidoiswin

Reputation: 2589

Abstracting out function across value classes

Let's say I have the following code for value classes:

class Meters(val x: Int) extends AnyVal {
  def +(m: Meters): Meters = new Meters(x + m.x)
}

class Seconds(val x: Int) extends AnyVal {
  def +(s: Seconds): Seconds = new Seconds(x + s.x)
}

Is there any way for me to remove duplication of the "+" methods? Something kind of like:

abstract class Units[T <: Units[T]](val x: Int) extends AnyVal {
  def +(other: T): T = T(x + other.x)
}

Except I can't inherit from value classes, and I definitely can't use T like a constructor.

Upvotes: 0

Views: 42

Answers (1)

Sleiman Jneidi
Sleiman Jneidi

Reputation: 23339

You can use a universal trait with a type class, lets start defining the trait.

trait Sum[T <: Sum[T]] extends Any {
  val x: Int
  def +(other: T)(implicit evidence : FromInt[T]): T = evidence.fromInt(x + other.x)
}

Now we need a type class that tell us how to go from an integer to some type, lets define this and call it FromInt

 trait FromInt[T] {
  def fromInt(x: Int) : T
}

now lets define the Meters value class which is as simple as class Meters(val x :Int) extends AnyVal with Sum[Meters] and in the companion object we can provide an implicit value of the type class we defined.

object Meters{
  implicit val intConstructable : FromInt[Meters] = new FromInt[Meters] {
  override def fromInt(x: Int) = new Meters(x)
}
}

and now we can just do

val added = new Meters(2) + new Meters(3)
println(added.x)

Upvotes: 2

Related Questions