Using Scala 2.8, you can write:
case class Vector3[T: Numeric](val x: T, val y: T, val z: T) { override def toString = "(%s, %s, %s)" format (x, y, z) def add(that: Vector3[T]) = new Vector3( plus(x, that.x), plus(y, that.y), plus(z, that.z) ) private def plus(x: T, y: T) = implicitly[Numeric[T]] plus (x, y) }
Let me explain. First, T: Numeric is a context boundary that implicitly provides an instance of Numeric[T] for your class.
The Numeric[T] function provides operations with numeric types,
trait Numeric[T] extends Ordering[T] { def plus(x: T, y: T): T def minus(x: T, y: T): T def times(x: T, y: T): T def negate(x: T): T // other operations omitted }
The expression implicitly[Numeric[T]] extracts this implicit context, so you can perform operations like plus on your specific arguments x, y, and z, as shown in the private method above.
Now you can build and add various instances of Vector3 , such as Int and Double :
scala> Vector3(1,2,3) add Vector3(4,5,6) res1: Vector3[Int] = (5, 7, 9) scala> Vector3(1.1, 2.2, 3.3) add Vector3(4.4, 5.5, 6.6) res2: Vector3[Double] = (5.5, 7.7, 9.899999999999999)
Note: you can use implicit conversions to convert values to Numeric[T].Ops instances, so instead of them you could write the following:
def add(that: Vector3[T]) = new Vector3(x + that.x, y + that.y, z + that.z)
I intentionally decided not to use these implicit conversions, as they (may) carry some performance limitations by creating temporary wrapper objects. The actual performance impact depends on the JVM (for example, the extent to which its support avoids analysis to avoid the actual distribution of objects on the heap). Using a bound context and implicitly avoids this potential overhead ... due to some verbosity.