Scala: Constraint on generic class type
Asked Answered
F

3

7

I am very new to Scala.

I want to implement a generic matrix class "class Matrix[T]". The only constraint on T should be that T should implement a "+" and a "*" mothod/function. How do I go about doing this?

For example I want to be able to use both Int, Double, and my own defined types e.g. Complex

I was thinking something along the lines:

class Matrix[T <: MatrixElement[T]](data: Array[Array[T]]) {
   def *(that: Matrix) = ..// code that uses "+" and "*" on the elements
}
abstract class MatrixElement[T] {
    def +(that: T): T
    def *(that: T): T 
}
implicit object DoubleMatrixElement extends MatrixElement[Double]{
    def +(that: Double): Double = this + that
    def *(that: Double): Double = this * that 
}
implicit object ComplexMatrixElement extends MatrixElement[Complex]{
    def +(that: Complex): Complex = this + that
    def *(that: Complex): Complex = this * that 
}

Everything type checks but I still can't instantiate a matrix. Am I missing an implicit constructor? How would I go about making that? Or am I completely wrong about my method?

Thanks in advance Troels

Fathom answered 16/2, 2010 at 14:1 Comment(0)
F
4

Finally found the answer :-) I think I wasn't that far off in my first try. Here it goes: (written for scala 2.8)

trait MatrixElement[T] {
    def +(that: T): T
    def *(that: T): T 
}

object MatrixElement {
    implicit def intToMatrixElement(x : Int) = new MatrixElement[Int] {
        def +(y : Int) = x + y
        def *(y : Int) = x * y
    }
    implicit def doubleToMatrixElement(x : Double) = new MatrixElement[Double] {
        def +(y : Double) = x + y
        def *(y : Double) = x * y
    }
    implicit def complexToMatrixElement(x : Complex) = new MatrixElement[Complex] {
        def +(y : Complex) = x + y
        def *(y : Complex) = x * y
    }
}

class Matrix[T  <% MatrixElement[T] : ClassManifest ](d: Array[Array[T]]) {
    def *(that: Matrix) = ..// code that uses "+" and "*" on the elements
}

Now I can do stuff like:

scala> new Matrix(Array(Array(1,0),Array(0,1)))
res0: Matrix[Int] = 
1 0 
0 1 

scala> new Matrix(Array(Array(new Complex(0),new Complex(1)),Array(new Complex(1),new Complex(0))))
res9: Matrix[Complex] = 
(0.0,0.0i) (1.0,0.0i) 
(1.0,0.0i) (0.0,0.0i) 
Fathom answered 18/2, 2010 at 0:19 Comment(0)
R
4

You can use Numeric for Scala 2.8 for that. It is describe here. It would replace MatrixElement and its implementations:

class Matrix[T : Numeric](data: Array[Array[T]]) {
   def *(that: Matrix[T]) = //
}
Roy answered 16/2, 2010 at 14:38 Comment(4)
I considered Numeric. But I don't really see how that would work for my own types e.g. Complex. I would think that Complex then would need to extend Numeric. Which first of all would require me to implement many more methods than just + and *. Amongst these ordering - As far as I know there is no strict ordering on complex numbers. The key point is that I need Matrix to work on any Types that just full fill that methods + and * are implemented.Fathom
There are a lot of methods to implement if you only need + and *. But you can still create something like Numeric with just these two methods. This should be two much work. (And maybe come back later and replace it with Numeric if worthwhile.)Roy
@troels You can always order by the real part, or just plain return "0" for all comparisons. And you can always "implement" the methods with error("Undefined method"). Notice, though, that Complex would not extend Numeric. Instead, there would be an instance of a Numeric[Complex].Sheepish
How does one go about implementing a subtype of Numeric[T]? I have been looking for at guide on this but haven't found any. Also posted it as a seperate question: #2275218Fathom
F
4

Finally found the answer :-) I think I wasn't that far off in my first try. Here it goes: (written for scala 2.8)

trait MatrixElement[T] {
    def +(that: T): T
    def *(that: T): T 
}

object MatrixElement {
    implicit def intToMatrixElement(x : Int) = new MatrixElement[Int] {
        def +(y : Int) = x + y
        def *(y : Int) = x * y
    }
    implicit def doubleToMatrixElement(x : Double) = new MatrixElement[Double] {
        def +(y : Double) = x + y
        def *(y : Double) = x * y
    }
    implicit def complexToMatrixElement(x : Complex) = new MatrixElement[Complex] {
        def +(y : Complex) = x + y
        def *(y : Complex) = x * y
    }
}

class Matrix[T  <% MatrixElement[T] : ClassManifest ](d: Array[Array[T]]) {
    def *(that: Matrix) = ..// code that uses "+" and "*" on the elements
}

Now I can do stuff like:

scala> new Matrix(Array(Array(1,0),Array(0,1)))
res0: Matrix[Int] = 
1 0 
0 1 

scala> new Matrix(Array(Array(new Complex(0),new Complex(1)),Array(new Complex(1),new Complex(0))))
res9: Matrix[Complex] = 
(0.0,0.0i) (1.0,0.0i) 
(1.0,0.0i) (0.0,0.0i) 
Fathom answered 18/2, 2010 at 0:19 Comment(0)
C
2

Here's how the Numeric solution would look:

// ': Numeric[T]' adds an implicit parameter to the constructor,
// which allows T to be used in arithmetic expressions.
class Matrix[T: Numeric](val data: Array[Array[T]]) {
   def *(that: Matrix[T]) = {
       val nt = implicitly[Numeric[T]]
       import nt._  // This imports an Implicit View to allow operator syntax

       this.data(0)(0) * that.data(0)(0)
       // etc
   }
}
Cryobiology answered 16/2, 2010 at 14:44 Comment(1)
T: Numeric[T] should be T: Numeric. Do you write this without REPL? :-)Roy

© 2022 - 2024 — McMap. All rights reserved.