3

I am using the jMonkeyEngine (Java Game Engine) in Scala, which works out pretty well so far, but now I am asking myself whether there is an elegant way of overloading operators for the Vector3f (and similar) classes. My first idea was to inherit from Vector3f and overload the operators, but Vector3f is final, so that is not an option. Next I thought maybe I could have a singleton/scala object with static methods that operate on Vector3f, like below, but that does not work either:

object VectorMath {
    def *(factor: Float, vector: Vector3f) = vector.mult(factor)
}

//and then somewhere
import VectorMath._
var v = new Vector3f(1,2,3);
var u = 1.2f * v; //this does not work, because Float does not have * overloaded for Vector3f
var u = VectorMath.*(1.2f, v); //this does work, but defeats the purpose

So all I can think of now is to wrap the Vector3f in a new Scala class and delegate the operator calls to the appropriate Java methods. This has, however, three downsides:

  1. I will have to do to a lot of back and forth conversions (a toVector3f and a fromVector3f method or something like that).
  2. This problem becomes even worse when I have arrays of Vector3f/ScalaVectors. Since the classes are not related (except by composition), I would have to manually cast every element of the array every time I call a method in jME.
  3. Even then I know of no way to overload an operator for my new ScalaVector class in such a way that I could have the factor in front, i.e. 1.2f * v.

My question: Can someone think of a way to make this more natural/elegant? How do you tackle similar problems? Or is there maybe a Scala syntax that I don know of to do these kinds of things? In C++ I would make a global operator that takes float and Vector3f as arguments and possibly friend it. Whats the Scala way to do this, or is that simply not possible?

4 Answers 4

12

Or is there maybe a Scala syntax that I don't know of to do these kinds of things?

Yeah, there are implicits:

class VectorMath(f: Float) { def * (v: Vector3f) = v mult f }
implicit def VectorMath(f: Float) = new VectorMath(f)

val v = new Vector3f(1,2,3)
1.2F * v
// treated as: VectorMath(1.2F).*(v)

Since Scala 2.10 the implicit conversion can also be written as:

implicit class VectorMath(f: Float) { def * (v: Vector3f) = v mult f }

Since 2.10 there are also value classes which are optimized by the compiler to get better performance:

implicit class VectorMath(val f: Float) extends AnyVal { def * (v: Vector3f) = v mult f }
Sign up to request clarification or add additional context in comments.

1 Comment

Note: this needs to be place in an object, not on the top scope. To use the implicit conversion you need to import that object. (This is perhaps clear to Scala experts, it was not for me, hence I am adding this comment).
3

In addition to what has been said about implicit classes above, starting from Scala 2.10, you will be able to use value classes to avoid creating an intermediate object.

Essentially, you just have to make VectorMath above extend AnyVal. If you don't use the VectorMath object as a return value of a method, and instead just invoke VectorMath operations, no object for VectorMath will be allocated at runtime.

Comments

1

Note: This can lead to very unreadable code.

You could use implicit conversions and create some sort of wrapper class around Vector3f (like the Scala standard library itself with several Java classes, such as the Java collections see here.

// Somewhere, for example in a package object
implicit def wrapVector3f (v: Vector3f) = new Vector3fWrapper ( v );

class Vector3fWrapper (v: Vector3f) {
    // Use the Vector3f object internally here
    // You can now delegate all methods to the underlying Vector3f object
    // For example:
    def * (v: Vector3f) = {
        // ...
    }

    // or:
    def * (f: Float) = {
        // ...
    }
}

Comments

-1

Everyone has already pointed out that you can implicitly add the operations to the Float on the left hand side, just wanted to point out you can make a right associative operator by putting a colon on the end of it.

class Vector3f(…) {
  …
  def *:(f: Float) = …
}

var v = new Vector3f(1,2,3);
var u = 1.2f *: v; //this works without any implicit magic or boxing overhead

it requires a small syntactic penalty, but is another option.

2 Comments

The problem with this approach is, that Vector3f is a final class from jMonkeyEngine. Therefore you can't extend it and add your own methods, how implicitly done in your solution.
k, so implicitly pimping it on is the solution – just wanted to show that there is an alternative approach available.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.