views:

80

answers:

1

I am writing some simple Vector and Matrix classes. They look like this:

// Vector with Floats
case class Vector3f(x: Float, y: Float, z: Float) {
  def +(v: Vector3f) = Vector3f(x + v.x, y + v.y, z + v.z)
}

// Vector with Doubles
case class Vector3d(x: Double, y: Double, z: Double) {
  def +(v: Vector3d) = Vector3d(x + v.x, y + v.y, z + v.z)
}

If I go on with further methods and classes like Point3f/d, Vector4f/d, Matrix3f/d, Matrix4f/d ... this is going to be a lot of work. Uff... So I thought generics could possible help here and remove redundancy from my code base. I thought of something like this:

// first I define a generic Vector class
case class Vector3[@specialized(Float, Double) T](x: T, y: T, z: T) {
   def +(v: Vector3[T]) = new Vector3[T](x + v.x, y + v.y, z + v.z)
}

// than I use some type aliases to hide the generic nature
type Vector3f = Vector3[Float]
type Vector3d = Vector3[Double]

The idea is that the scala compiler generates specialized classes for Vector3[Float] and Vector3[Double] similar as a C++ template would do. Unfortunately I have to put some type bound on the type parameter [T] of class Vector3 such that the operator + is defined on T. My question: How can I write Vector3[Float] that it has the same performance characteristics as Vector3f? Context: I would like to use the Vector3f / Vector3d classes in collision detection code ... so performance does matter for me.

+4  A: 

Use a context bound of Fractional:

case class Vector3[@specialized(Float, Double) T : Fractional](x: T, y: T, z: T)  { ...

then within the body of the class, get an instance of the arithmetic operators:

  val fractOps = implicitly[Fractional[T]]

lastly import its members into the scope of the class:

  import fractOps._

Thereafter you can write ordinary infix operations on values of type T used within the class. Sadly, you will have to use fractOps.div(a, b) instead of a / b for division.

Randall Schulz
Thanks for your solution but there seems to be a performance penalty. The vector addition public Vector3f $plus(Vector3f v) { return new Vector3f(x() + v.x(), y() + v.y(), z() + v.z()); }becomes public Vector3<T> $plus(Vector3<T> v) { return new Vector3(fractOps().plus(x(), v.x()), fractOps().plus(y(), v.y()), fractOps().plus(z(), v.z()), this.Vector3$$evidence$1); }I guess I should not use generics to implement vector and matrix classes.
gruenewa
@gruenewa: You need to look at the bytecode and understand what current native-code JVMs (HotSpot, e.g.) can do. I think it's likely that this will all be in-line native code in a running application. However, without the specialization, there will be boxing of the primitive Float and Double, which may be enough to prevent the JIT compiler from fully optimizing the code.
Randall Schulz
@gruenewa Did you try to measure if there's a noticeable performance difference? That's the only way to know for sure - it might be that `scalac` and/or the JIT are optimizing things so that there isn't any significant difference.
Jesper