4
votes

So I'm learning Scala at the moment, and I'm trying to create an abstract vector class with a vector-space of 3 (x,y,z coordinates). I'm trying to add two of these vectors together with the following code:


package math

class Vector3[T](ax:T,ay:T,az:T) {
  def x = ax
  def y = ay
  def z = az
  override def toString = "&lt"+x+", "+y+", "+z+"&gt"
  def add(that: Vector3[T]) = new Vector3(x+that.x, y+that.y, z+that.z)
}

The problem is I keep getting this error:

error: type mismatch;
found : T
required: String
def add(that: Vector3[T]) = new Vector3(x+that.x, y+that.y, z+that.z)

I've tried commenting out the "toString" method above, but that doesn't seem to have any effect. Can anyone tell me what I'm doing wrong?

4

4 Answers

8
votes

Using Scala 2.8, you could 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 bound that implicitly provides a Numeric[T] instance to your class.

The Numeric[T] trait provides operations on 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]] retrieves this implicit context such that you can perform the operations such as plus on your concrete arguments x, y and z, as illustrated in the private method above.

You can now construct and add different instantiations of Vector3 such as with Int's and Double's:

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)

Side-note: It's possible to use implicit conversions to convert values to Numeric[T].Ops instances such that the following could be written instead:

  def add(that: Vector3[T]) = new Vector3(x + that.x, y + that.y, z + that.z)

I've deliberately chosen not to use these implicit conversions since they (may) incur some performance penalty by creating temporary wrapper objects. Actual performance impact depends on the JVM (e.g., to which extent its supports escape analysis to avoid actual object allocation on heap). Using a context bound and implicitly avoids this potential overhead ... at the cost of some verbosity.

8
votes

You have not constrained the type parameter T and so the compiler is falling back to the interpretation of + as String concatenation.

6
votes

The problem is T. It's of type Any, but Any doesn't have a + operator. The error about String is a bit miss leading. So you're going to have to define the min bound to a type that does.

4
votes

Both the answers from @sblundy and @Randall Schulz are correct, of course, but in case you need some more concrete advice about how to constrain T then how about:

class Vector3[T <% Double](ax:T,ay:T,az:T) {
...
}