Reputation: 2589
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
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