I'm currently thinking about refactoring my personal linear-algebra package. One thing that really bothers me is:
Currently I only support Vectors and Matrices that consist of float
s. Now I'd like to add in support for int
s, double
s and possibly even for boolean
s, but I'll have to think about that.
One thing tho: I do not want to write code multiple times. In fact, that's one thing I truly hate in programming. I want a single source of responsibility for a certain code/behaviour.
For most, well, all operations a Vector has there is a certain pattern. It doesn't matter if I add two Two-dimensional-vectors that hold int
s or float
s, the operation is always the same.
Now, naively I though "Well, how difficult can this be?!" Turns out, for me as a Scala-noobie, it's not that easy.
I began with:
trait Vector[Vec <: Vector]
{
def add(v: Vec): Unit
def subtract(v: Vec): Unit
def multiply(v: Vec): Unit
def multiply(s: Numeric): Unit
def dot(v: Vec): Numeric
def length(): Numeric
def distance(v: Vec): Numeric
def normalise(): Unit
def divide(length: Numeric): Unit
def toArray(): Array[Numeric]
}
My thoughts: Adding in a bound would help me, as I proceed.
Example:
abstract class Vector2(var x: Numeric, var y: Numeric) extends Vector[Vector2]
{
override def add(v: Vector2): Unit =
{
this.x += v.x
this.y += v.y
}
//...
}
Then I wanted to create sub-types like:
class IntVector2(var x: Int, var y: Int) extends Vector2
and be done with it.
Problem begins here:
abstract class Vector2(var x: Numeric, var y: Numeric) extends Vector[Vector2]
{
override def add(v: Vector2): Unit =
{
this.x += v.x // **here is the problem **
this.y += v.y
}
//...
}
It says
Type mismatch, expected: String, actual: Numeric
I thought I'd be brainy by using Numeric as the upper-bound, as I assumed an addition would be defined for all... how wrong was I?
How can I solve this? Any ideas?
Also, before I forget...
Assume cases where I need to use a helper, like math.sqrt(...)
. What to do there?
Currently (only implemented for float
s, mind!) I do this:
def length(): Float =
{
math.sqrt(x * x + y * y).toFloat
}
Now if I want to have the same for double
s and int
s, how can I make this generic? Like, without .toFloat
Also, I'm fully aware that with boolean
s I would get a problem, as some operations are simply not defined at all... No reason to freak out, I hope :)
You should really consider looking inside scala standard collection library. Give attention to all classes that ends with Like: TraversableLike, SeqLike, SetLike and so on. They use higher-order types and typeclass polymorphism. Notice all uses of implicit arguments to methods like def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That
.
Here is the project that provide many numeric abstractions and may be used as a reference.